├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── e2e ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── ngsw-config.json ├── package-lock.json ├── package.json ├── resources ├── icon.icns ├── icon.ico └── icons │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 16x16.png │ ├── 24x24.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ └── 64x64.png ├── src ├── CNAME ├── app │ ├── core │ │ ├── http │ │ │ ├── http.service.spec.ts │ │ │ └── http.service.ts │ │ └── services │ │ │ ├── map-cache │ │ │ └── map-cache.service.ts │ │ │ ├── mmp │ │ │ └── mmp.service.ts │ │ │ ├── notification │ │ │ └── notification.service.ts │ │ │ ├── settings │ │ │ └── settings.service.ts │ │ │ ├── shortcuts │ │ │ └── shortcuts.service.ts │ │ │ ├── storage │ │ │ └── storage.service.ts │ │ │ └── utils │ │ │ └── utils.service.ts │ ├── modules │ │ ├── about │ │ │ ├── about-routing.module.ts │ │ │ ├── about.module.ts │ │ │ ├── components │ │ │ │ ├── footer │ │ │ │ │ ├── footer.component.html │ │ │ │ │ ├── footer.component.scss │ │ │ │ │ ├── footer.component.spec.ts │ │ │ │ │ └── footer.component.ts │ │ │ │ ├── header │ │ │ │ │ ├── header.component.html │ │ │ │ │ ├── header.component.scss │ │ │ │ │ ├── header.component.spec.ts │ │ │ │ │ └── header.component.ts │ │ │ │ └── jumbotron │ │ │ │ │ ├── jumbotron.component.html │ │ │ │ │ ├── jumbotron.component.scss │ │ │ │ │ ├── jumbotron.component.spec.ts │ │ │ │ │ └── jumbotron.component.ts │ │ │ └── pages │ │ │ │ └── about │ │ │ │ ├── about.component.html │ │ │ │ ├── about.component.scss │ │ │ │ └── about.component.ts │ │ └── application │ │ │ ├── application-routing.module.ts │ │ │ ├── application.module.ts │ │ │ ├── components │ │ │ ├── color-panels │ │ │ │ ├── color-panels.component.html │ │ │ │ ├── color-panels.component.scss │ │ │ │ └── color-panels.component.ts │ │ │ ├── floating-buttons │ │ │ │ ├── floating-buttons.component.html │ │ │ │ ├── floating-buttons.component.scss │ │ │ │ └── floating-buttons.component.ts │ │ │ ├── information │ │ │ │ ├── information.component.html │ │ │ │ ├── information.component.scss │ │ │ │ └── information.component.ts │ │ │ ├── map │ │ │ │ ├── map.component.html │ │ │ │ ├── map.component.scss │ │ │ │ └── map.component.ts │ │ │ ├── slider-panels │ │ │ │ ├── slider-panels.component.html │ │ │ │ ├── slider-panels.component.scss │ │ │ │ └── slider-panels.component.ts │ │ │ ├── tabs │ │ │ │ ├── tabs.component.html │ │ │ │ ├── tabs.component.scss │ │ │ │ ├── tabs.component.spec.ts │ │ │ │ └── tabs.component.ts │ │ │ └── toolbar │ │ │ │ ├── toolbar.component.html │ │ │ │ ├── toolbar.component.scss │ │ │ │ ├── toolbar.component.spec.ts │ │ │ │ └── toolbar.component.ts │ │ │ └── pages │ │ │ ├── application │ │ │ ├── application.component.html │ │ │ ├── application.component.scss │ │ │ └── application.component.ts │ │ │ ├── settings │ │ │ ├── settings.component.html │ │ │ ├── settings.component.scss │ │ │ └── settings.component.ts │ │ │ └── shortcuts │ │ │ ├── shortcuts.component.html │ │ │ ├── shortcuts.component.scss │ │ │ └── shortcuts.component.ts │ ├── root-routing.module.ts │ ├── root.component.html │ ├── root.component.scss │ ├── root.component.ts │ ├── root.module.ts │ └── shared │ │ ├── animations │ │ └── route.animation.ts │ │ ├── components │ │ └── .gitkeep │ │ ├── models │ │ ├── cached-map.model.ts │ │ ├── mmp.model.ts │ │ └── settings.model.ts │ │ ├── pipes │ │ └── .gitkeep │ │ └── shared.module.ts ├── assets │ ├── data │ │ └── settings.json │ ├── font │ │ └── source-sans-pro │ │ │ ├── OFL.txt │ │ │ ├── SourceSansPro-Black.ttf │ │ │ ├── SourceSansPro-BlackItalic.ttf │ │ │ ├── SourceSansPro-Bold.ttf │ │ │ ├── SourceSansPro-BoldItalic.ttf │ │ │ ├── SourceSansPro-ExtraLight.ttf │ │ │ ├── SourceSansPro-ExtraLightItalic.ttf │ │ │ ├── SourceSansPro-Italic.ttf │ │ │ ├── SourceSansPro-Light.ttf │ │ │ ├── SourceSansPro-LightItalic.ttf │ │ │ ├── SourceSansPro-Regular.ttf │ │ │ ├── SourceSansPro-SemiBold.ttf │ │ │ └── SourceSansPro-SemiBoldItalic.ttf │ ├── i18n │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── it.json │ │ ├── pt-br.json │ │ ├── zh-cn.json │ │ └── zh-tw.json │ ├── icons │ │ ├── icon-1024x1024.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ └── light-icon.png │ └── images │ │ ├── business-plan.png │ │ ├── radial-tree.png │ │ ├── readme-header.png │ │ ├── screens.png │ │ └── solar-system.png ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── index.html ├── main.ts ├── manifest.webmanifest ├── polyfills.ts ├── styles.scss ├── test.ts ├── theme.scss ├── tsconfig.app.json ├── tsconfig.spec.json ├── tslint.json └── typings.d.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 2 | patreon: # Replace with a single Patreon username 3 | open_collective: mindmapp 4 | ko_fi: # Replace with a single Ko-fi username 5 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 6 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 7 | liberapay: # Replace with a single Liberapay username 8 | issuehunt: # Replace with a single IssueHunt username 9 | otechie: # Replace with a single Otechie username 10 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 11 | -------------------------------------------------------------------------------- /.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 | /builds 8 | /documentation 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # IDEs and editors 14 | /.idea 15 | .project 16 | .classpath 17 | .c9/ 18 | *.launch 19 | .settings/ 20 | *.sublime-workspace 21 | 22 | # IDE - VSCode 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | 29 | # misc 30 | /.sass-cache 31 | /connect.lock 32 | /coverage 33 | /libpeerconnection.log 34 | npm-debug.log 35 | testem.log 36 | /typings 37 | 38 | # e2e 39 | /e2e/*.js 40 | /e2e/*.map 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Omar Desogus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "mindmapp": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "mindmapp", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "style": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "src/tsconfig.app.json", 25 | "assets": [ 26 | "src/assets", 27 | "src/CNAME", 28 | "src/manifest.webmanifest" 29 | ], 30 | "styles": [ 31 | "src/styles.scss", 32 | "src/theme.scss", 33 | "node_modules/material-design-icons/iconfont/material-icons.css" 34 | ], 35 | "scripts": [], 36 | "serviceWorker": true, 37 | "ngswConfigPath": "ngsw-config.json" 38 | }, 39 | "configurations": { 40 | "production": { 41 | "fileReplacements": [ 42 | { 43 | "replace": "src/environments/environment.ts", 44 | "with": "src/environments/environment.prod.ts" 45 | } 46 | ], 47 | "optimization": true, 48 | "outputHashing": "all", 49 | "sourceMap": false, 50 | "extractCss": true, 51 | "namedChunks": false, 52 | "aot": true, 53 | "extractLicenses": true, 54 | "vendorChunk": false, 55 | "buildOptimizer": true, 56 | "budgets": [ 57 | { 58 | "type": "initial", 59 | "maximumWarning": "2mb", 60 | "maximumError": "5mb" 61 | } 62 | ] 63 | } 64 | } 65 | }, 66 | "serve": { 67 | "builder": "@angular-devkit/build-angular:dev-server", 68 | "options": { 69 | "browserTarget": "mindmapp:build" 70 | }, 71 | "configurations": { 72 | "production": { 73 | "browserTarget": "mindmapp:build:production" 74 | } 75 | } 76 | }, 77 | "extract-i18n": { 78 | "builder": "@angular-devkit/build-angular:extract-i18n", 79 | "options": { 80 | "browserTarget": "mindmapp:build" 81 | } 82 | }, 83 | "test": { 84 | "builder": "@angular-devkit/build-angular:karma", 85 | "options": { 86 | "main": "src/test.ts", 87 | "polyfills": "src/polyfills.ts", 88 | "tsConfig": "src/tsconfig.spec.json", 89 | "karmaConfig": "karma.conf.js", 90 | "assets": [ 91 | "src/assets", 92 | "src/CNAME", 93 | "src/manifest.webmanifest" 94 | ], 95 | "styles": [ 96 | "src/styles.scss", 97 | "src/theme.scss", 98 | "node_modules/material-design-icons/iconfont/material-icons.css" 99 | ], 100 | "scripts": [] 101 | } 102 | }, 103 | "lint": { 104 | "builder": "@angular-devkit/build-angular:tslint", 105 | "options": { 106 | "tsConfig": [ 107 | "src/tsconfig.app.json", 108 | "src/tsconfig.spec.json", 109 | "e2e/tsconfig.json" 110 | ], 111 | "exclude": [ 112 | "**/node_modules/**" 113 | ] 114 | } 115 | }, 116 | "e2e": { 117 | "builder": "@angular-devkit/build-angular:protractor", 118 | "options": { 119 | "protractorConfig": "e2e/protractor.conf.js", 120 | "devServerTarget": "mindmapp:serve" 121 | }, 122 | "configurations": { 123 | "production": { 124 | "devServerTarget": "mindmapp:serve:production" 125 | } 126 | } 127 | }, 128 | "deploy": { 129 | "builder": "angular-cli-ghpages:deploy", 130 | "options": {} 131 | } 132 | } 133 | } 134 | }, 135 | "defaultProject": "mindmapp" 136 | } 137 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to ciao!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/ciao'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/service-worker/config/schema.json", 3 | "index": "/index.html", 4 | "assetGroups": [ 5 | { 6 | "name": "app", 7 | "installMode": "prefetch", 8 | "resources": { 9 | "files": [ 10 | "/favicon.ico", 11 | "/index.html", 12 | "/*.css", 13 | "/*.js" 14 | ] 15 | } 16 | }, 17 | { 18 | "name": "assets", 19 | "installMode": "lazy", 20 | "updateMode": "prefetch", 21 | "resources": { 22 | "files": [ 23 | "/assets/**", 24 | "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)" 25 | ] 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mindmapp", 3 | "version": "0.9.2", 4 | "description": "Web application to draw mind maps.", 5 | "homepage": "https://mindmapp.cedoor.dev", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/Mindmapp/mindmapp.git" 9 | }, 10 | "author": { 11 | "name": "Omar Desogus", 12 | "email": "me@cedoor.dev", 13 | "url": "https://cedoor.dev" 14 | }, 15 | "license": "MIT", 16 | "keywords": [ 17 | "mmp", 18 | "mindmap", 19 | "node", 20 | "branch", 21 | "map", 22 | "mind", 23 | "infovis" 24 | ], 25 | "main": "app.js", 26 | "scripts": { 27 | "start": "ng serve", 28 | "build": "ng build --prod", 29 | "test": "ng test", 30 | "lint": "ng lint", 31 | "e2e": "ng e2e", 32 | "doc": "compodoc -p src/tsconfig.app.json", 33 | "deploy": "ng deploy", 34 | "postinstall": "opencollective-postinstall || true" 35 | }, 36 | "dependencies": { 37 | "@angular/animations": "^9.0.4", 38 | "@angular/cdk": "^9.1.0", 39 | "@angular/common": "^9.0.4", 40 | "@angular/compiler": "^9.0.4", 41 | "@angular/core": "^9.0.4", 42 | "@angular/forms": "^9.0.4", 43 | "@angular/material": "^9.1.0", 44 | "@angular/platform-browser": "^9.0.4", 45 | "@angular/platform-browser-dynamic": "^9.0.4", 46 | "@angular/pwa": "^0.900.4", 47 | "@angular/router": "^9.0.4", 48 | "@angular/service-worker": "^9.0.4", 49 | "@fortawesome/angular-fontawesome": "^0.6.0", 50 | "@fortawesome/fontawesome-svg-core": "^1.2.27", 51 | "@fortawesome/free-brands-svg-icons": "^5.12.1", 52 | "@fortawesome/free-solid-svg-icons": "^5.12.1", 53 | "@ngx-translate/core": "^12.1.2", 54 | "@ngx-translate/http-loader": "^4.0.0", 55 | "angular-cli-ghpages": "^0.6.2", 56 | "angular2-hotkeys": "^2.1.5", 57 | "hammerjs": "^2.0.8", 58 | "jspdf": "^1.5.3", 59 | "material-design-icons": "^3.0.1", 60 | "mmp": "0.2.18", 61 | "ngx-color-picker": "^9.0.0", 62 | "opencollective-postinstall": "^2.0.2", 63 | "rxjs": "~6.5.4", 64 | "tslib": "^1.11.1", 65 | "zone.js": "^0.10.2" 66 | }, 67 | "devDependencies": { 68 | "@angular-devkit/build-angular": "~0.900.4", 69 | "@angular-devkit/schematics": "^9.0.4", 70 | "@angular/cli": "~9.0.4", 71 | "@angular/compiler-cli": "^9.0.4", 72 | "@angular/language-service": "^9.0.4", 73 | "@compodoc/compodoc": "^1.1.11", 74 | "@types/jasmine": "~3.5.7", 75 | "@types/jasminewd2": "~2.0.8", 76 | "@types/node": "^13.7.7", 77 | "codelyzer": "^5.2.1", 78 | "core-js": "^3.2.1", 79 | "jasmine-core": "~3.5.0", 80 | "jasmine-spec-reporter": "~4.2.1", 81 | "karma": "~4.4.1", 82 | "karma-chrome-launcher": "~3.1.0", 83 | "karma-coverage-istanbul-reporter": "~2.1.1", 84 | "karma-jasmine": "~3.1.1", 85 | "karma-jasmine-html-reporter": "^1.5.2", 86 | "minimist": "^1.2.3", 87 | "protractor": "~5.4.0", 88 | "ts-node": "~8.6.2", 89 | "tslint": "~6.0.0", 90 | "typescript": "~3.7.5" 91 | }, 92 | "collective": { 93 | "type": "opencollective", 94 | "url": "https://opencollective.com/mindmapp" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icon.icns -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icon.ico -------------------------------------------------------------------------------- /resources/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/1024x1024.png -------------------------------------------------------------------------------- /resources/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/128x128.png -------------------------------------------------------------------------------- /resources/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/16x16.png -------------------------------------------------------------------------------- /resources/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/24x24.png -------------------------------------------------------------------------------- /resources/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/256x256.png -------------------------------------------------------------------------------- /resources/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/32x32.png -------------------------------------------------------------------------------- /resources/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/48x48.png -------------------------------------------------------------------------------- /resources/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/512x512.png -------------------------------------------------------------------------------- /resources/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/resources/icons/64x64.png -------------------------------------------------------------------------------- /src/CNAME: -------------------------------------------------------------------------------- 1 | mindmapp.cedoor.dev 2 | -------------------------------------------------------------------------------- /src/app/core/http/http.service.spec.ts: -------------------------------------------------------------------------------- 1 | import {TestBed} from '@angular/core/testing' 2 | 3 | import {HttpService} from './http.service' 4 | 5 | describe('EventService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})) 7 | 8 | it('should be created', () => { 9 | const service: HttpService = TestBed.get(HttpService) 10 | expect(service).toBeTruthy() 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/app/core/http/http.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {HttpClient} from '@angular/common/http' 3 | 4 | /** 5 | * All the API urls used with the http service. 6 | */ 7 | export enum API_URL { 8 | LOCAL_ASSETS = './assets/data' 9 | } 10 | 11 | @Injectable({ 12 | providedIn: 'root' 13 | }) 14 | export class HttpService { 15 | 16 | constructor (private httpClient: HttpClient) { 17 | } 18 | 19 | /** 20 | * Constructs a `GET` request that returns the response body as a JSON object. 21 | */ 22 | public get (apiUrl: API_URL, endpoint: string): Promise { 23 | return this.httpClient.get(`${apiUrl}/${endpoint}`).toPromise() 24 | } 25 | 26 | /** 27 | * Constructs a `POST` request that interprets the body as a JSON object and 28 | * returns the response body as a JSON object. 29 | */ 30 | public async post (apiUrl: API_URL, endpoint: string, body: any): Promise { 31 | return this.httpClient.post(`${apiUrl}/${endpoint}`, body).toPromise() 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/core/services/map-cache/map-cache.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {MmpService} from '../mmp/mmp.service' 3 | import {BehaviorSubject, Observable} from 'rxjs' 4 | import {StorageService} from '../storage/storage.service' 5 | import {CachedMap, CachedMapEntry} from '../../../shared/models/cached-map.model' 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class MapCacheService { 11 | 12 | // Observable of behavior subject with the attached map key. 13 | public attachedMap: Observable 14 | // The title of the browser window. 15 | private readonly windowTitle: string 16 | private readonly attachedMapSubject: BehaviorSubject 17 | 18 | constructor (private mmpService: MmpService, 19 | private storageService: StorageService) { 20 | // Save the window title. 21 | this.windowTitle = window.document.title 22 | // Initialization of the behavior subjects. 23 | this.attachedMapSubject = new BehaviorSubject(null) 24 | this.attachedMap = this.attachedMapSubject.asObservable() 25 | } 26 | 27 | /** 28 | * If there are cached maps, then attach the last cached map. 29 | * Otherwise set the attached map status to `null`. 30 | */ 31 | public async init (): Promise { 32 | // Get all the storage entries. 33 | const storageEntries: any[] = await this.storageService.getAllEntries() 34 | // Set the eventual last cached map variables. 35 | let lastCachedMap: CachedMap 36 | let lastCachedKey: string 37 | 38 | for (const [key, value] of storageEntries) { 39 | if (key.includes('map-')) { 40 | if (!lastCachedMap || value.lastModified > lastCachedMap.lastModified) { 41 | lastCachedMap = value 42 | lastCachedKey = key 43 | } 44 | } 45 | } 46 | 47 | // If there is not cached maps attach the new current map. 48 | if (!lastCachedMap) { 49 | this.attachNewMap() 50 | return 51 | } 52 | 53 | // Attach the last cached map. 54 | this.attachMap({ 55 | key: lastCachedKey, 56 | cachedMap: lastCachedMap 57 | }) 58 | 59 | // Create a new map in the application with the last cached map data. 60 | this.mmpService.new(lastCachedMap.data) 61 | } 62 | 63 | /** 64 | * Add current new application map to cache and attach it. 65 | */ 66 | public async attachNewMap (): Promise { 67 | const key = this.createKey() 68 | 69 | const cachedMap: CachedMap = { 70 | data: this.mmpService.exportAsJSON(), 71 | lastModified: Date.now() 72 | } 73 | 74 | await this.storageService.set(key, cachedMap) 75 | this.attachMap({key, cachedMap}) 76 | } 77 | 78 | /** 79 | * Attach a map. 80 | */ 81 | public attachMap (cachedMapEntry: CachedMapEntry): void { 82 | this.attachedMapSubject.next(cachedMapEntry) 83 | } 84 | 85 | /** 86 | * Update the attached map. 87 | */ 88 | public async updateAttachedMap (): Promise { 89 | const cachedMapEntry: CachedMapEntry = this.getAttachedMap() 90 | 91 | const cachedMap: CachedMap = { 92 | data: this.mmpService.exportAsJSON(), 93 | lastModified: Date.now() 94 | } 95 | 96 | await this.storageService.set(cachedMapEntry.key, cachedMap) 97 | this.attachMap({key: cachedMapEntry.key, cachedMap}) 98 | } 99 | 100 | /** 101 | * Remove a cached map from the storage. 102 | */ 103 | public async removeCachedMap (key: string): Promise { 104 | // Remove the map from the storage, if it exists. 105 | await this.storageService.remove(key) 106 | } 107 | 108 | /** 109 | * Return all the cached map entries (cachedMap + key) of the storage. 110 | */ 111 | public async getCachedMapEntries (): Promise { 112 | // Get all the storage entries. 113 | const storageEntries: any[] = await this.storageService.getAllEntries() || [] 114 | 115 | return storageEntries.filter((entry: any[]) => { 116 | return entry[0].includes('map-') 117 | }).map((entry: any[]) => { 118 | return { 119 | key: entry[0], 120 | cachedMap: entry[1] 121 | } 122 | }) 123 | } 124 | 125 | /** 126 | * Return the attached cached map key, otherwise if there is no attached maps return `null`. 127 | */ 128 | public getAttachedMap (): CachedMapEntry { 129 | return this.attachedMapSubject.getValue() 130 | } 131 | 132 | /** 133 | * Return the key of the map in the storage: `map-` + current timestamp. 134 | */ 135 | private createKey (): string { 136 | return `map-${Date.now().toString()}` 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/app/core/services/notification/notification.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {MatSnackBar} from '@angular/material/snack-bar' 3 | import {BehaviorSubject, Observable} from 'rxjs' 4 | import {UtilsService} from '../utils/utils.service' 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class NotificationService { 10 | 11 | public message: Observable 12 | private readonly messageSubject: BehaviorSubject 13 | private messageTimeoutId: number | null 14 | 15 | constructor (private matSnackBar: MatSnackBar, 16 | private utilsService: UtilsService) { 17 | // Initialization of the behavior subjects. 18 | this.messageSubject = new BehaviorSubject('') 19 | this.message = this.messageSubject.asObservable() 20 | } 21 | 22 | /** 23 | * Show a message with a material snack bar. 24 | */ 25 | public async showSnackBarMessage (message: string, values?: any, duration: number = 5000) { 26 | const action = await this.utilsService.translate('GENERAL.DISMISS') 27 | message = await this.utilsService.translate(message, values) 28 | 29 | this.matSnackBar.open(message, action, { 30 | duration, 31 | panelClass: 'snackbar' 32 | }) 33 | } 34 | 35 | /** 36 | * Set a message. 37 | */ 38 | public async setMessage (message: string, duration: number = 4000) { 39 | message = await this.utilsService.translate(message) 40 | this.messageSubject.next(message) 41 | 42 | if (duration) { 43 | if (this.messageTimeoutId !== null) { 44 | clearTimeout(this.messageTimeoutId) 45 | } 46 | 47 | this.messageTimeoutId = setTimeout(() => { 48 | this.messageSubject.next('') 49 | this.messageTimeoutId = null 50 | }, duration) 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/app/core/services/settings/settings.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {STORAGE_KEYS, StorageService} from '../storage/storage.service' 3 | import {Settings} from '../../../shared/models/settings.model' 4 | import {UtilsService} from '../utils/utils.service' 5 | import {API_URL, HttpService} from '../../http/http.service' 6 | import {NotificationService} from '../notification/notification.service' 7 | import {BehaviorSubject, Observable} from 'rxjs' 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class SettingsService { 13 | 14 | public static readonly LANGUAGES = ['en', 'fr', 'de', 'it', 'zh-tw', 'zh-cn', 'es', 'pt-br'] 15 | 16 | public settings: Observable 17 | private settingsSubject: BehaviorSubject 18 | 19 | constructor (private storageService: StorageService, 20 | private notificationService: NotificationService, 21 | private httpService: HttpService) { 22 | // Initialization of the behavior subjects. 23 | this.settingsSubject = new BehaviorSubject(null) 24 | this.settings = this.settingsSubject.asObservable() 25 | } 26 | 27 | /** 28 | * Initialize settings with the default or cached values and return them. 29 | */ 30 | public async init (): Promise<{ settings: Settings, isFirstTime: boolean }> { 31 | const defaultSettings: Settings = await this.getDefaultSettings() 32 | const settings: Settings = await this.storageService.get(STORAGE_KEYS.SETTINGS) 33 | 34 | // Check if there are settings in the storage and if these settings have an old structure. 35 | if (settings !== null && UtilsService.isSameJSONStructure(defaultSettings, settings)) { 36 | this.settingsSubject.next(settings) 37 | 38 | return { 39 | settings, 40 | isFirstTime: false 41 | } 42 | } 43 | 44 | // Save the default settings. 45 | await this.storageService.set(STORAGE_KEYS.SETTINGS, defaultSettings) 46 | this.settingsSubject.next(defaultSettings) 47 | 48 | return { 49 | settings: defaultSettings, 50 | isFirstTime: true 51 | } 52 | } 53 | 54 | /** 55 | * Update the settings in the storage. 56 | */ 57 | public async updateCachedSettings (settings: Settings): Promise { 58 | await this.storageService.set(STORAGE_KEYS.SETTINGS, settings) 59 | 60 | this.settingsSubject.next(settings) 61 | } 62 | 63 | /** 64 | * Return the current settings. 65 | */ 66 | public getCachedSettings (): Settings | null { 67 | return this.settingsSubject.getValue() 68 | } 69 | 70 | /** 71 | * Return the default settings. 72 | */ 73 | private getDefaultSettings (): Promise { 74 | return this.httpService.get(API_URL.LOCAL_ASSETS, 'settings.json') 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/app/core/services/shortcuts/shortcuts.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {MmpService} from '../mmp/mmp.service' 3 | import {Router} from '@angular/router' 4 | import {Hotkey, HotkeysService} from 'angular2-hotkeys' 5 | import {MapCacheService} from '../map-cache/map-cache.service' 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class ShortcutsService { 11 | 12 | private hotKeys: Hotkey[] 13 | 14 | constructor (private mmpService: MmpService, 15 | private hotkeysService: HotkeysService, 16 | private mapCacheService: MapCacheService, 17 | private router: Router) { 18 | } 19 | 20 | /** 21 | * Add all global hot keys of the application. 22 | */ 23 | public init () { 24 | this.hotKeys = [{ 25 | keys: 'esc', 26 | description: 'TOOLTIPS.ESC', 27 | callback: () => { 28 | this.router.navigate(['app']) 29 | } 30 | }, { 31 | keys: '?', 32 | description: 'TOOLTIPS.SHORTCUTS', 33 | callback: () => { 34 | this.router.navigate(['app', 'shortcuts']) 35 | } 36 | }, { 37 | keys: 'alt+s', 38 | description: 'TOOLTIPS.SETTINGS', 39 | callback: () => { 40 | this.router.navigate(['app', 'settings']) 41 | } 42 | }, { 43 | keys: 'f2', 44 | description: 'TOOLTIPS.EDIT_NODE', 45 | callback: () => { 46 | this.mmpService.editNode() 47 | } 48 | }, { 49 | keys: 'n', 50 | description: 'TOOLTIPS.NEW_MAP', 51 | callback: () => { 52 | this.mmpService.new() 53 | } 54 | }, { 55 | keys: 'c', 56 | description: 'TOOLTIPS.CENTER_MAP', 57 | callback: () => { 58 | this.mmpService.center() 59 | } 60 | }, { 61 | keys: '+', 62 | description: 'TOOLTIPS.ADD_NODE', 63 | callback: () => { 64 | this.mmpService.addNode() 65 | } 66 | }, { 67 | keys: '-', 68 | description: 'TOOLTIPS.REMOVE_NODE', 69 | callback: () => { 70 | this.mmpService.removeNode() 71 | } 72 | }, { 73 | keys: 'ctrl+c', 74 | description: 'TOOLTIPS.COPY_NODE', 75 | callback: () => { 76 | this.mmpService.copyNode() 77 | } 78 | }, { 79 | keys: 'ctrl+x', 80 | description: 'TOOLTIPS.CUT_NODE', 81 | callback: () => { 82 | this.mmpService.cutNode() 83 | } 84 | }, { 85 | keys: 'ctrl+v', 86 | description: 'TOOLTIPS.PASTE_NODE', 87 | callback: () => { 88 | this.mmpService.pasteNode() 89 | } 90 | }, { 91 | keys: 'ctrl+=', 92 | description: 'TOOLTIPS.ZOOM_IN_MAP', 93 | callback: () => { 94 | this.mmpService.zoomIn() 95 | } 96 | }, { 97 | keys: 'ctrl+-', 98 | description: 'TOOLTIPS.ZOOM_OUT_MAP', 99 | callback: () => { 100 | this.mmpService.zoomOut() 101 | } 102 | }, { 103 | keys: 'left', 104 | description: 'TOOLTIPS.SELECT_NODE_ON_THE_LEFT', 105 | callback: () => { 106 | this.mmpService.selectNode('left') 107 | } 108 | }, { 109 | keys: 'right', 110 | description: 'TOOLTIPS.SELECT_NODE_ON_THE_RIGHT', 111 | callback: () => { 112 | this.mmpService.selectNode('right') 113 | } 114 | }, { 115 | keys: 'up', 116 | description: 'TOOLTIPS.SELECT_NODE_BELOW', 117 | callback: () => { 118 | this.mmpService.selectNode('up') 119 | } 120 | }, { 121 | keys: 'down', 122 | description: 'TOOLTIPS.SELECT_NODE_ABOVE', 123 | callback: () => { 124 | this.mmpService.selectNode('down') 125 | } 126 | }, { 127 | keys: 'alt+left', 128 | description: 'TOOLTIPS.MOVE_NODE_TO_THE_LEFT', 129 | callback: () => { 130 | this.mmpService.moveNodeTo('left') 131 | } 132 | }, { 133 | keys: 'alt+right', 134 | description: 'TOOLTIPS.MOVE_NODE_TO_THE_RIGHT', 135 | callback: () => { 136 | this.mmpService.moveNodeTo('right') 137 | } 138 | }, { 139 | keys: 'alt+up', 140 | description: 'TOOLTIPS.MOVE_NODE_UPWARD', 141 | callback: () => { 142 | this.mmpService.moveNodeTo('up') 143 | } 144 | }, { 145 | keys: 'alt+down', 146 | description: 'TOOLTIPS.MOVE_NODE_DOWN', 147 | callback: () => { 148 | this.mmpService.moveNodeTo('down') 149 | } 150 | }, { 151 | keys: 'ctrl+i', 152 | description: 'TOOLTIPS.IMPORT_MAP', 153 | callback: () => { 154 | this.mmpService.importMap() 155 | } 156 | }, { 157 | keys: 'ctrl+e', 158 | description: 'TOOLTIPS.EXPORT_MAP', 159 | callback: () => { 160 | this.mmpService.exportMap() 161 | } 162 | }, { 163 | keys: 'ctrl+z', 164 | description: 'TOOLTIPS.UNDO_MAP', 165 | callback: () => { 166 | this.mmpService.undo() 167 | } 168 | }, { 169 | keys: 'ctrl+shift+z', 170 | description: 'TOOLTIPS.REDO_MAP', 171 | callback: () => { 172 | this.mmpService.redo() 173 | } 174 | }].map(this.getHotKey) 175 | 176 | this.hotkeysService.add(this.hotKeys) 177 | } 178 | 179 | /** 180 | * Return all the shortcuts. 181 | */ 182 | public getHotKeys (): Hotkey[] { 183 | return this.hotKeys 184 | } 185 | 186 | 187 | /** 188 | * Get some shortcut parameters and return the corresponding hot key. 189 | */ 190 | private getHotKey (options: { 191 | keys: string | string[], 192 | description: string, 193 | callback: (event?: KeyboardEvent) => void 194 | }) { 195 | return new Hotkey(options.keys, (event: KeyboardEvent) => { 196 | options.callback(event) 197 | 198 | return false 199 | }, undefined, options.description) 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /src/app/core/services/storage/storage.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {UtilsService} from '../utils/utils.service' 3 | 4 | /** 5 | * Enumerative of the possible keys present in the storage 6 | */ 7 | export enum STORAGE_KEYS { 8 | SETTINGS = 'settings' 9 | } 10 | 11 | @Injectable({ 12 | providedIn: 'root' 13 | }) 14 | export class StorageService { 15 | 16 | // The storage used in the service. 17 | private readonly storage: Storage 18 | 19 | /** 20 | * Initialize the storage service setting the default storage. 21 | */ 22 | constructor () { 23 | // Use the window local storage. 24 | this.storage = window.localStorage 25 | } 26 | 27 | /** 28 | * Return the value or the values based on the keys passed as parameters. 29 | */ 30 | public async get (keys: string | string[]): Promise { 31 | if (typeof keys === 'string') { 32 | return this.parseItem(this.storage.getItem(keys)) 33 | } 34 | 35 | const items: any[] = [] 36 | 37 | for (const key of Object.keys(this.storage)) { 38 | if (keys.includes(key)) { 39 | items.push(await this.get(key)) 40 | } 41 | } 42 | 43 | return items && items.length > 0 ? items : null 44 | } 45 | 46 | /** 47 | * Return all the saved values in the storage. 48 | */ 49 | public async getAll (): Promise { 50 | return Object.values(this.storage).map((item: string) => { 51 | return this.parseItem(item) 52 | }) || null 53 | } 54 | 55 | /** 56 | * Return all the saved keys in the storage. 57 | */ 58 | public async getAllEntries (): Promise { 59 | return Object.entries(this.storage).map((entry) => { 60 | entry[1] = this.parseItem(entry[1]) 61 | 62 | return entry 63 | }) || null 64 | } 65 | 66 | /** 67 | * Save an item in the storage. 68 | */ 69 | public async set (key: string, item: any): Promise { 70 | this.storage.setItem(key, JSON.stringify(item)) 71 | } 72 | 73 | /** 74 | * Remove an item from the storage. 75 | */ 76 | public async remove (key: string): Promise { 77 | this.storage.removeItem(key) 78 | } 79 | 80 | /** 81 | * Check if an item exist in the storage. Return true if it exist, false otherwise. 82 | */ 83 | public async exist (key: string): Promise { 84 | return !!this.storage.getItem(key) 85 | } 86 | 87 | 88 | /** 89 | * Remove all the items from the storage. 90 | */ 91 | public async clear (): Promise { 92 | this.storage.clear() 93 | } 94 | 95 | /** 96 | * Check if there are items in the storage. Return true if there are items, false otherwise. 97 | */ 98 | public async isEmpty (): Promise { 99 | const items: any[] = await this.getAll() 100 | 101 | return items && items.length > 0 102 | } 103 | 104 | /** 105 | * Parse the storage item and return the correct type. 106 | */ 107 | private parseItem (item: string): any { 108 | if (UtilsService.isJSONString(item)) { 109 | return JSON.parse(item) 110 | } else if (!Number.isNaN(+item)) { 111 | return +item 112 | } else if (item === 'true' || item === 'false') { 113 | return item === 'true' 114 | } else { 115 | return item 116 | } 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/app/core/services/utils/utils.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core' 2 | import {Observable} from 'rxjs' 3 | import {TranslateService} from '@ngx-translate/core' 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class UtilsService { 9 | 10 | constructor (private translateService: TranslateService) { 11 | } 12 | 13 | /** 14 | * Return the word with the first letter capitalized. 15 | */ 16 | public static capitalizeWord (word: string): string { 17 | return word.charAt(0).toUpperCase() + word.toLowerCase().slice(1) 18 | } 19 | 20 | /** 21 | * Return an observable for drop events for images. 22 | */ 23 | public static observableDroppedImages (): Observable { 24 | return new Observable((subscriber) => { 25 | window.document.ondragover = (event: DragEvent) => { 26 | event.preventDefault() 27 | } 28 | 29 | window.document.body.ondrop = (event: DragEvent) => { 30 | event.preventDefault() 31 | 32 | if (event.dataTransfer.files[0]) { 33 | const fileReader = new FileReader() 34 | 35 | fileReader.onload = () => { 36 | subscriber.next(fileReader.result.toString()) 37 | } 38 | 39 | fileReader.onerror = subscriber.error 40 | 41 | fileReader.readAsDataURL(event.dataTransfer.files[0]) 42 | } else { 43 | subscriber.next(event.dataTransfer.getData('text/html').match(/src\s*=\s*"(.+?)"/)[1]) 44 | 45 | } 46 | } 47 | }) 48 | } 49 | 50 | /** 51 | * Upload a file with a fake input click. 52 | */ 53 | public static uploadFile (accept: string[] | string = 'application/json'): Promise { 54 | return new Promise((resolve, reject) => { 55 | const fakeInput = document.createElement('input') 56 | 57 | fakeInput.type = 'file' 58 | fakeInput.accept = Array.isArray(accept) ? accept.join(', ') : accept 59 | 60 | document.body.appendChild(fakeInput) 61 | 62 | fakeInput.click() 63 | 64 | fakeInput.oninput = () => { 65 | const fileReader = new FileReader() 66 | 67 | fileReader.onload = () => { 68 | resolve(fileReader.result.toString()) 69 | } 70 | fileReader.onerror = reject 71 | 72 | if (accept === 'application/json') { 73 | fileReader.readAsText(fakeInput.files[0]) 74 | } else { 75 | fileReader.readAsDataURL(fakeInput.files[0]) 76 | } 77 | } 78 | 79 | fakeInput.onerror = reject 80 | 81 | document.body.removeChild(fakeInput) 82 | }) 83 | } 84 | 85 | /** 86 | * Download a file with a fake link click. 87 | */ 88 | public static downloadFile (name: string, content: string) { 89 | const fakeLink = document.createElement('a') 90 | 91 | fakeLink.href = content 92 | fakeLink.download = name 93 | 94 | document.body.appendChild(fakeLink) 95 | 96 | fakeLink.click() 97 | 98 | document.body.removeChild(fakeLink) 99 | } 100 | 101 | /** 102 | * Return the HTML image element from an image URI. 103 | */ 104 | public static imageFromUri (uri: string): Promise { 105 | return new Promise((resolve, reject) => { 106 | const image = new Image() 107 | 108 | image.onload = () => { 109 | resolve(image) 110 | } 111 | image.onerror = reject 112 | image.src = uri 113 | }) 114 | } 115 | 116 | /** 117 | * Return the status of the full screen mode. 118 | */ 119 | public static isFullScreen (): boolean { 120 | return !!window.document.fullscreenElement 121 | } 122 | 123 | /** 124 | * Toggle the full screen mode. 125 | */ 126 | public static toggleFullScreen () { 127 | if (UtilsService.isFullScreen()) { 128 | window.document.exitFullscreen() 129 | } else { 130 | window.document.querySelector('html').requestFullscreen() 131 | } 132 | } 133 | 134 | /** 135 | * Return true if the string is a JSON Object. 136 | */ 137 | public static isJSONString (JSONString: string) { 138 | try { 139 | JSON.parse(JSONString) 140 | } catch (e) { 141 | return false 142 | } 143 | return true 144 | } 145 | 146 | /** 147 | * Return true if the two objects have the same structure (same keys). 148 | */ 149 | public static isSameJSONStructure (json1: object, json2: object): boolean { 150 | function checkObjectStructure (object1: object, object2: object): boolean { 151 | for (const key in object1) { 152 | if (!object1.hasOwnProperty(key) || !object2.hasOwnProperty(key)) { 153 | return false 154 | } 155 | 156 | if (typeof object1[key] === 'object') { 157 | if (!checkObjectStructure(object1[key], object2[key])) { 158 | return false 159 | } 160 | } 161 | } 162 | 163 | return true 164 | } 165 | 166 | return checkObjectStructure(json1, json2) && checkObjectStructure(json2, json1) 167 | } 168 | 169 | /** 170 | * Return a translated string with given message and values. 171 | */ 172 | public translate (message: string, values?: any): Promise { 173 | return this.translateService.get(message, values).toPromise() 174 | } 175 | 176 | /** 177 | * Show a dialog window to confirm a choice. 178 | */ 179 | public async confirmDialog (message: string): Promise { 180 | message = await this.translate(message) 181 | 182 | return confirm(message) 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /src/app/modules/about/about-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core' 2 | import {RouterModule, Routes} from '@angular/router' 3 | import {AboutComponent} from './pages/about/about.component' 4 | 5 | const routes: Routes = [{ 6 | path: '', 7 | component: AboutComponent 8 | }] 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class AboutRoutingModule { 15 | } 16 | -------------------------------------------------------------------------------- /src/app/modules/about/about.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core' 2 | import {AboutRoutingModule} from './about-routing.module' 3 | import {SharedModule} from '../../shared/shared.module' 4 | import {AboutComponent} from './pages/about/about.component' 5 | import {JumbotronComponent} from './components/jumbotron/jumbotron.component' 6 | import {HeaderComponent} from './components/header/header.component' 7 | import {FooterComponent} from './components/footer/footer.component' 8 | 9 | @NgModule({ 10 | imports: [ 11 | SharedModule, 12 | AboutRoutingModule 13 | ], 14 | declarations: [ 15 | AboutComponent, 16 | JumbotronComponent, 17 | HeaderComponent, 18 | FooterComponent 19 | ] 20 | }) 21 | export class AboutModule { 22 | } 23 | -------------------------------------------------------------------------------- /src/app/modules/about/components/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /src/app/modules/about/components/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | footer.footer { 2 | padding: 20px 0; 3 | background-color: #2c2c2c; 4 | color: whitesmoke; 5 | 6 | div.container { 7 | display: flex; 8 | justify-content: space-between; 9 | align-items: center; 10 | flex-wrap: wrap; 11 | font-size: 16px; 12 | 13 | a { 14 | text-decoration: none; 15 | color: whitesmoke; 16 | } 17 | 18 | a:hover { 19 | opacity: .8; 20 | } 21 | 22 | div.spacer { 23 | flex: 1 1 auto; 24 | } 25 | 26 | div.license { 27 | display: inline-block; 28 | white-space: nowrap; 29 | padding: 8px 30px 8px 0; 30 | } 31 | 32 | div.links { 33 | padding: 8px 0; 34 | 35 | > a { 36 | display: inline-block; 37 | white-space: nowrap; 38 | padding-right: 15px; 39 | } 40 | } 41 | 42 | div.languages { 43 | padding: 8px 0; 44 | 45 | mat-select::ng-deep { 46 | width: fit-content !important; 47 | min-width: 100%; 48 | 49 | .mat-select-value { 50 | width: fit-content; 51 | min-width: 2ch; 52 | max-width: 25ch; 53 | color: whitesmoke; 54 | } 55 | 56 | .mat-select-arrow { 57 | color: whitesmoke; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/app/modules/about/components/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, ComponentFixture, TestBed} from '@angular/core/testing' 2 | 3 | import {FooterComponent} from './footer.component' 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent 7 | let fixture: ComponentFixture 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [FooterComponent] 12 | }) 13 | .compileComponents() 14 | })) 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent) 18 | component = fixture.componentInstance 19 | fixture.detectChanges() 20 | }) 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/app/modules/about/components/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import * as packageJson from '../../../../../../package.json' 3 | import {UtilsService} from '../../../../core/services/utils/utils.service' 4 | import {SettingsService} from '../../../../core/services/settings/settings.service' 5 | import {TranslateService} from '@ngx-translate/core' 6 | import {Settings} from '../../../../shared/models/settings.model' 7 | 8 | @Component({ 9 | selector: 'mindmapp-footer', 10 | templateUrl: './footer.component.html', 11 | styleUrls: ['./footer.component.scss'] 12 | }) 13 | export class FooterComponent implements OnInit { 14 | 15 | public settings: Settings 16 | public languages: string[] 17 | 18 | public projectName: string 19 | public currentYear: string 20 | public projectAuthor: any 21 | 22 | constructor (private settingsService: SettingsService, 23 | private translateService: TranslateService) { 24 | } 25 | 26 | public ngOnInit () { 27 | this.settings = this.settingsService.getCachedSettings() 28 | this.languages = SettingsService.LANGUAGES 29 | 30 | this.projectName = UtilsService.capitalizeWord(packageJson.name) 31 | this.currentYear = new Date().getFullYear().toString() 32 | this.projectAuthor = packageJson.author 33 | } 34 | 35 | public async updateLanguage () { 36 | await this.settingsService.updateCachedSettings(this.settings) 37 | 38 | this.translateService.use(this.settings.general.language) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/app/modules/about/components/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 | 25 |
26 | -------------------------------------------------------------------------------- /src/app/modules/about/components/header/header.component.scss: -------------------------------------------------------------------------------- 1 | @import "~src/theme.scss"; 2 | 3 | header.header { 4 | z-index: 99; 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | background-color: white; 10 | 11 | nav { 12 | display: flex; 13 | flex-wrap: wrap; 14 | align-items: center; 15 | padding: 16px; 16 | 17 | span.toolbar-spacer { 18 | flex: 1 1 auto; 19 | } 20 | 21 | div.vertical-line { 22 | width: 1px; 23 | height: 35px; 24 | margin: 0 10px; 25 | background-color: #c2c2c2; 26 | } 27 | 28 | a.link { 29 | cursor: pointer; 30 | color: mat-color($app-primary); 31 | padding-right: 12px; 32 | } 33 | 34 | img.logo { 35 | padding-right: 10px; 36 | } 37 | 38 | h3 { 39 | margin: 0 20px 3px 0; 40 | font-weight: normal; 41 | } 42 | 43 | a.anchor { 44 | font-size: 16px; 45 | margin-top: 2px; 46 | margin-left: 5px; 47 | } 48 | 49 | @media only screen and (max-width: 600px) { 50 | a.anchor { 51 | display: none; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/modules/about/components/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, ComponentFixture, TestBed} from '@angular/core/testing' 2 | 3 | import {HeaderComponent} from './header.component' 4 | 5 | describe('HeaderComponent', () => { 6 | let component: HeaderComponent 7 | let fixture: ComponentFixture 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [HeaderComponent] 12 | }) 13 | .compileComponents() 14 | })) 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HeaderComponent) 18 | component = fixture.componentInstance 19 | fixture.detectChanges() 20 | }) 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/app/modules/about/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {UtilsService} from '../../../../core/services/utils/utils.service' 3 | import * as packageJson from '../../../../../../package.json' 4 | import {faGithub, faGitter} from '@fortawesome/free-brands-svg-icons' 5 | 6 | @Component({ 7 | selector: 'mindmapp-header', 8 | templateUrl: './header.component.html', 9 | styleUrls: ['./header.component.scss'] 10 | }) 11 | export class HeaderComponent implements OnInit { 12 | 13 | public projectName: string 14 | 15 | public faGithub = faGithub 16 | public faGitter = faGitter 17 | 18 | constructor () { 19 | } 20 | 21 | public ngOnInit () { 22 | this.projectName = UtilsService.capitalizeWord(packageJson.name) 23 | } 24 | 25 | public slide (selector: string, event: Event) { 26 | if (selector) { 27 | event.preventDefault() 28 | const element = document.querySelector(selector) 29 | 30 | window.scrollTo({ 31 | behavior: 'smooth', 32 | top: element.getBoundingClientRect().top + window.scrollY - 70 33 | }) 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/app/modules/about/components/jumbotron/jumbotron.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | 5 | {{ projectName }} 6 |

7 | 8 |
9 |

{{ "PAGES.ABOUT.INTRODUCTION.APPLICATION_DESCRIPTION" | translate }}

10 | 11 | 12 | check 13 |

{{ "PAGES.ABOUT.INTRODUCTION.APPLICATION_PROPERTIES.0" | translate }}

14 |
15 | 16 | check 17 |

{{ "PAGES.ABOUT.INTRODUCTION.APPLICATION_PROPERTIES.1" | translate }}

18 |
19 | 20 | check 21 |

{{ "PAGES.ABOUT.INTRODUCTION.APPLICATION_PROPERTIES.2" | translate }}

22 |
23 | 24 | check 25 |

{{ "PAGES.ABOUT.INTRODUCTION.APPLICATION_PROPERTIES.3" | translate }}

26 |
27 |
28 |
29 | 30 | 31 | {{ "GENERAL.TRY_IT" | translate | uppercase }} 32 | 33 |
34 |
35 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /src/app/modules/about/components/jumbotron/jumbotron.component.scss: -------------------------------------------------------------------------------- 1 | @import "~src/theme.scss"; 2 | 3 | section.jumbotron { 4 | background-color: mat-color($app-primary); 5 | padding: 100px 0 40px 0; 6 | text-align: center; 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | flex-wrap: wrap; 11 | 12 | h1, h4, p { 13 | color: whitesmoke; 14 | } 15 | 16 | > div { 17 | padding: 20px 35px; 18 | 19 | > div { 20 | padding-bottom: 20px; 21 | 22 | > p { 23 | margin: 0; 24 | } 25 | 26 | > mat-list { 27 | text-align: left; 28 | margin-bottom: 10px; 29 | 30 | mat-list-item { 31 | height: 30px; 32 | } 33 | 34 | mat-icon { 35 | color: #74c13f; 36 | } 37 | } 38 | } 39 | 40 | > h1 { 41 | font-family: "Comic Sans MS", cursive, sans-serif; 42 | margin: 0 0 24px; 43 | font-size: 42px; 44 | font-weight: 400; 45 | line-height: 54px; 46 | text-shadow: 0 4px 4px rgba(0, 0, 0, .22); 47 | 48 | > img { 49 | width: 46px; 50 | } 51 | } 52 | 53 | > a { 54 | width: 150px; 55 | font-weight: bold; 56 | color: mat-color($app-primary); 57 | margin-bottom: 10px; 58 | } 59 | 60 | > img { 61 | max-width: 500px; 62 | width: 100%; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/app/modules/about/components/jumbotron/jumbotron.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, ComponentFixture, TestBed} from '@angular/core/testing' 2 | 3 | import {JumbotronComponent} from './jumbotron.component' 4 | 5 | describe('JumbotronComponent', () => { 6 | let component: JumbotronComponent 7 | let fixture: ComponentFixture 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [JumbotronComponent] 12 | }) 13 | .compileComponents() 14 | })) 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(JumbotronComponent) 18 | component = fixture.componentInstance 19 | fixture.detectChanges() 20 | }) 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/app/modules/about/components/jumbotron/jumbotron.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {UtilsService} from '../../../../core/services/utils/utils.service' 3 | import * as packageJson from '../../../../../../package.json' 4 | 5 | @Component({ 6 | selector: 'mindmapp-jumbotron', 7 | templateUrl: './jumbotron.component.html', 8 | styleUrls: ['./jumbotron.component.scss'] 9 | }) 10 | export class JumbotronComponent implements OnInit { 11 | 12 | public projectName: string 13 | 14 | constructor () { 15 | } 16 | 17 | public ngOnInit () { 18 | this.projectName = UtilsService.capitalizeWord(packageJson.name) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/modules/about/pages/about/about.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 |
7 |

8 | 9 |   {{ 'PAGES.ABOUT.SECTIONS.0.TITLE' | translate }} 10 |

11 |

{{ 'PAGES.ABOUT.SECTIONS.0.SUBTITLE' | translate }}

12 |
13 | 14 | 15 | 16 | {{ 'PAGES.ABOUT.SECTIONS.0.CARDS.0.TITLE' | translate }} 17 | 18 | 19 | Solar system 20 | 21 |

{{ 'PAGES.ABOUT.SECTIONS.0.CARDS.0.CONTENT' | translate }}

22 |
23 |
24 | 25 | 26 | 27 | {{ 'PAGES.ABOUT.SECTIONS.0.CARDS.1.TITLE' | translate }} 28 | 29 | 30 | Radial tree 31 | 32 |

{{ 'PAGES.ABOUT.SECTIONS.0.CARDS.1.CONTENT' | translate }}

33 |
34 |
35 | 36 | 37 | 38 | {{ 'PAGES.ABOUT.SECTIONS.0.CARDS.2.TITLE' | translate }} 39 | 40 | 41 | Business plan 42 | 43 |

{{ 'PAGES.ABOUT.SECTIONS.0.CARDS.2.CONTENT' | translate }}

44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |

52 | 53 |   {{ 'PAGES.ABOUT.SECTIONS.1.TITLE' | translate }} 54 |

55 |

{{ 'PAGES.ABOUT.SECTIONS.1.SUBTITLE' | translate }}

56 |
57 | 58 | 59 | 60 | 61 | {{ 'PAGES.ABOUT.SECTIONS.1.CARDS.0.TITLE' | translate }} 62 | 63 | 64 | 65 |

{{ 'PAGES.ABOUT.SECTIONS.1.CARDS.0.CONTENT' | translate }}

66 |
67 |
68 | 69 | 70 | 71 | 72 | {{ 'PAGES.ABOUT.SECTIONS.1.CARDS.1.TITLE' | translate }} 73 | 74 | 75 | 76 |

{{ 'PAGES.ABOUT.SECTIONS.1.CARDS.1.CONTENT' | translate }}

77 |
78 |
79 | 80 | 81 | 82 | 83 | {{ 'PAGES.ABOUT.SECTIONS.1.CARDS.2.TITLE' | translate }} 84 | 85 | 86 | 87 |

{{ 'PAGES.ABOUT.SECTIONS.1.CARDS.2.CONTENT' | translate }}

88 |
89 |
90 |
91 |
92 |
93 | 94 |
95 |
96 |

97 | 98 |   {{ 'PAGES.ABOUT.SECTIONS.2.TITLE' | translate }} 99 |

100 |

{{ 'PAGES.ABOUT.SECTIONS.2.SUBTITLE' | translate }}

101 | 102 | 103 | link 104 | Open Collective 105 | 106 | 107 | link 108 | PayPal 109 | 110 | 111 |
112 |
113 | 114 | 115 |
116 | -------------------------------------------------------------------------------- /src/app/modules/about/pages/about/about.component.scss: -------------------------------------------------------------------------------- 1 | @import "~src/theme.scss"; 2 | 3 | div.about { 4 | height: 100%; 5 | 6 | section { 7 | position: relative; 8 | } 9 | 10 | section:nth-child(odd) { 11 | background-color: white; 12 | } 13 | 14 | section:nth-child(even) { 15 | background-color: #fafafa; 16 | } 17 | 18 | section.about-section { 19 | > div { 20 | padding: 20px 15px 40px 15px; 21 | 22 | h1 { 23 | color: mat-color($app-primary); 24 | } 25 | 26 | div.cards { 27 | display: flex; 28 | flex-wrap: wrap; 29 | align-items: center; 30 | 31 | mat-card { 32 | flex: 1; 33 | min-width: 200px; 34 | margin: 10px; 35 | 36 | fa-icon { 37 | color: #565656; 38 | } 39 | 40 | ::ng-deep mat-card-header > div { 41 | margin-left: 0 !important; 42 | } 43 | } 44 | } 45 | 46 | mat-list { 47 | margin-top: -10px; 48 | 49 | mat-list-item { 50 | height: 40px; 51 | 52 | a { 53 | color: mat-color($app-primary); 54 | text-decoration: none; 55 | padding-left: 10px; 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/app/modules/about/pages/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {faBrain, faChartLine, faCheck, faCogs, faHeart, faRocket} from '@fortawesome/free-solid-svg-icons' 3 | 4 | @Component({ 5 | selector: 'mindmapp-about', 6 | templateUrl: './about.component.html', 7 | styleUrls: ['./about.component.scss'] 8 | }) 9 | export class AboutComponent implements OnInit { 10 | 11 | public faBrain = faBrain 12 | public faRocket = faRocket 13 | public faHeart = faHeart 14 | public faChartLine = faChartLine 15 | public faCogs = faCogs 16 | public faCheck = faCheck 17 | 18 | constructor () { 19 | } 20 | 21 | public ngOnInit () { 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/modules/application/application-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core' 2 | import {RouterModule, Routes} from '@angular/router' 3 | import {ApplicationComponent} from './pages/application/application.component' 4 | import {SettingsComponent} from './pages/settings/settings.component' 5 | import {ShortcutsComponent} from './pages/shortcuts/shortcuts.component' 6 | 7 | const routes: Routes = [{ 8 | path: '', 9 | component: ApplicationComponent 10 | }, { 11 | path: 'settings', 12 | component: SettingsComponent 13 | }, { 14 | path: 'shortcuts', 15 | component: ShortcutsComponent 16 | }] 17 | 18 | @NgModule({ 19 | imports: [RouterModule.forChild(routes)], 20 | exports: [RouterModule] 21 | }) 22 | export class ApplicationRoutingModule { 23 | } 24 | -------------------------------------------------------------------------------- /src/app/modules/application/application.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core' 2 | import {ApplicationRoutingModule} from './application-routing.module' 3 | import {SharedModule} from '../../shared/shared.module' 4 | import {ColorPanelsComponent} from './components/color-panels/color-panels.component' 5 | import {SliderPanelsComponent} from './components/slider-panels/slider-panels.component' 6 | import {FloatingButtonsComponent} from './components/floating-buttons/floating-buttons.component' 7 | import {MapComponent} from './components/map/map.component' 8 | import {InformationComponent} from './components/information/information.component' 9 | import {ApplicationComponent} from './pages/application/application.component' 10 | import {SettingsComponent} from './pages/settings/settings.component' 11 | import {ShortcutsComponent} from './pages/shortcuts/shortcuts.component' 12 | import {ToolbarComponent} from './components/toolbar/toolbar.component' 13 | import {TabsComponent} from './components/tabs/tabs.component' 14 | import {MatDialogModule} from '@angular/material/dialog' 15 | import {MatSlideToggleModule} from '@angular/material/slide-toggle' 16 | import {MatMenuModule} from '@angular/material/menu' 17 | import {MatInputModule} from '@angular/material/input' 18 | import {MatTabsModule} from '@angular/material/tabs' 19 | 20 | @NgModule({ 21 | imports: [ 22 | SharedModule, 23 | MatDialogModule, 24 | MatInputModule, 25 | MatMenuModule, 26 | MatSlideToggleModule, 27 | MatTabsModule, 28 | ApplicationRoutingModule 29 | ], 30 | declarations: [ 31 | ApplicationComponent, 32 | SettingsComponent, 33 | ShortcutsComponent, 34 | ColorPanelsComponent, 35 | FloatingButtonsComponent, 36 | InformationComponent, 37 | MapComponent, 38 | SliderPanelsComponent, 39 | ToolbarComponent, 40 | TabsComponent 41 | ] 42 | }) 43 | export class ApplicationModule { 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/app/modules/application/components/color-panels/color-panels.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 17 | 29 | 42 |
43 |
44 | -------------------------------------------------------------------------------- /src/app/modules/application/components/color-panels/color-panels.component.scss: -------------------------------------------------------------------------------- 1 | div.colors-panel { 2 | 3 | div.background { 4 | z-index: 2; 5 | position: absolute; 6 | visibility: hidden; 7 | top: 0; 8 | left: 0; 9 | height: 100%; 10 | width: 100%; 11 | } 12 | 13 | div.panel { 14 | z-index: 3; 15 | position: absolute; 16 | right: 20px; 17 | top: 44%; 18 | 19 | input { 20 | cursor: pointer; 21 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .3), 22 | 0 2px 2px 0 rgba(0, 0, 0, .18), 23 | 0 1px 5px 0 rgba(0, 0, 0, .16); 24 | display: block; 25 | margin-bottom: 8px; 26 | font-size: 0; 27 | padding: 16px; 28 | border: 0; 29 | border-radius: 30px; 30 | user-select: none; 31 | } 32 | 33 | input:focus { 34 | outline: none; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/app/modules/application/components/color-panels/color-panels.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core' 2 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 3 | 4 | @Component({ 5 | selector: 'mindmapp-colors-panel', 6 | templateUrl: './color-panels.component.html', 7 | styleUrls: ['./color-panels.component.scss'] 8 | }) 9 | export class ColorPanelsComponent implements OnInit { 10 | 11 | @Input() public node: any 12 | 13 | @ViewChild('background') public background: ElementRef 14 | 15 | public options: any 16 | 17 | constructor (public mmpService: MmpService) { 18 | } 19 | 20 | ngOnInit () { 21 | this.options = { 22 | width: '250px', 23 | presetColors: [ 24 | '#666666', 25 | '#f5f5f5', 26 | '#f44336', 27 | '#E91E63', 28 | '#9C27B0', 29 | '#673AB7', 30 | '#3F51B5', 31 | '#2196F3', 32 | '#03A9F4', 33 | '#00BCD4', 34 | '#009688', 35 | '#4CAF50', 36 | '#8BC34A', 37 | '#CDDC39', 38 | '#FFEB3B', 39 | '#FFC107', 40 | '#FF9800', 41 | '#FF5722', 42 | '#795548', 43 | '#9E9E9E', 44 | '#607D8B' 45 | ] 46 | } 47 | } 48 | 49 | public colorPickerChange (property, value) { 50 | this.mmpService.updateNode(property, value, true) 51 | } 52 | 53 | public colorPickerToggleChange (opening, property, value) { 54 | this.background.nativeElement.style.visibility = opening ? 'visible' : 'hidden' 55 | 56 | if (!opening) { 57 | this.mmpService.updateNode(property, value) 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/app/modules/application/components/floating-buttons/floating-buttons.component.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 12 | 13 | 18 | 23 | 24 | 29 | 30 | 35 | 40 |
41 | -------------------------------------------------------------------------------- /src/app/modules/application/components/floating-buttons/floating-buttons.component.scss: -------------------------------------------------------------------------------- 1 | div.floating-buttons { 2 | .mat-mini-fab { 3 | z-index: 2; 4 | position: absolute; 5 | } 6 | 7 | .add-node, .remove-node, 8 | .undo, .redo { 9 | top: 115px; 10 | } 11 | 12 | .add-node { 13 | left: 15px; 14 | } 15 | 16 | .remove-node { 17 | left: 65px; 18 | } 19 | 20 | .undo { 21 | right: 65px; 22 | } 23 | 24 | .redo { 25 | right: 15px; 26 | } 27 | 28 | .zoom-in, .zoom-out, .center { 29 | right: 15px; 30 | } 31 | 32 | .zoom-out { 33 | bottom: 100px; 34 | } 35 | 36 | .zoom-in { 37 | bottom: 150px; 38 | } 39 | 40 | .center { 41 | bottom: 45px; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/app/modules/application/components/floating-buttons/floating-buttons.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 3 | 4 | @Component({ 5 | selector: 'mindmapp-floating-buttons', 6 | templateUrl: './floating-buttons.component.html', 7 | styleUrls: ['./floating-buttons.component.scss'] 8 | }) 9 | export class FloatingButtonsComponent implements OnInit { 10 | 11 | constructor (public mmpService: MmpService) { 12 | } 13 | 14 | ngOnInit () { 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/app/modules/application/components/information/information.component.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/app/modules/application/components/information/information.component.scss: -------------------------------------------------------------------------------- 1 | div.footer { 2 | width: 100%; 3 | color: #555555; 4 | border-top: 1px solid lightgray; 5 | background-color: white; 6 | 7 | div.footer-content { 8 | display: flex; 9 | padding: 4px 10px; 10 | 11 | span.footer-spacer { 12 | flex: 1 1 auto; 13 | } 14 | 15 | div.vertical-line { 16 | width: 1px; 17 | margin: 0 10px; 18 | background-color: #c2c2c2; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/modules/application/components/information/information.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core' 2 | import {NotificationService} from '../../../../core/services/notification/notification.service' 3 | 4 | @Component({ 5 | selector: 'mindmapp-information', 6 | templateUrl: './information.component.html', 7 | styleUrls: ['./information.component.scss'] 8 | }) 9 | export class InformationComponent { 10 | 11 | constructor (public notificationsService: NotificationService) { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/app/modules/application/components/map/map.component.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /src/app/modules/application/components/map/map.component.scss: -------------------------------------------------------------------------------- 1 | div.map { 2 | height: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/modules/application/components/map/map.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'mindmapp-map', 5 | templateUrl: './map.component.html', 6 | styleUrls: ['./map.component.scss'] 7 | }) 8 | export class MapComponent implements OnInit { 9 | 10 | constructor () { 11 | } 12 | 13 | ngOnInit () { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/app/modules/application/components/slider-panels/slider-panels.component.html: -------------------------------------------------------------------------------- 1 |
2 | 10 | 11 | 19 | 20 |
21 | -------------------------------------------------------------------------------- /src/app/modules/application/components/slider-panels/slider-panels.component.scss: -------------------------------------------------------------------------------- 1 | div.sliders-panel { 2 | 3 | .mat-slider-vertical { 4 | z-index: 2; 5 | position: absolute; 6 | height: 180px; 7 | top: 40%; 8 | } 9 | 10 | .font-size { 11 | left: 5px; 12 | } 13 | 14 | .image-size { 15 | left: 35px; 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/app/modules/application/components/slider-panels/slider-panels.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input} from '@angular/core' 2 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 3 | 4 | @Component({ 5 | selector: 'mindmapp-sliders-panel', 6 | templateUrl: './slider-panels.component.html', 7 | styleUrls: ['./slider-panels.component.scss'] 8 | }) 9 | export class SliderPanelsComponent { 10 | 11 | @Input() public node: any 12 | 13 | constructor (public mmpService: MmpService) { 14 | } 15 | 16 | public updateNodeFontSize (event: any, graphic?: boolean) { 17 | const value = parseInt(event.source.value) 18 | 19 | this.mmpService.updateNode('fontSize', value, graphic) 20 | } 21 | 22 | public updateNodeImageSize (event: any, graphic?: boolean) { 23 | const value = parseInt(event.source.value) 24 | 25 | this.mmpService.updateNode('imageSize', value, graphic) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/app/modules/application/components/tabs/tabs.component.html: -------------------------------------------------------------------------------- 1 |
2 |
6 | {{ cachedMapEntry.cachedMap.data[0].name }} 7 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /src/app/modules/application/components/tabs/tabs.component.scss: -------------------------------------------------------------------------------- 1 | @import "~src/theme.scss"; 2 | 3 | div.tabs { 4 | overflow-x: auto; 5 | display: flex; 6 | width: 100%; 7 | border-bottom: 1px solid lightgray; 8 | 9 | div.tab { 10 | flex-shrink: 0; 11 | display: flex; 12 | justify-content: space-between; 13 | align-items: center; 14 | cursor: pointer; 15 | width: 150px; 16 | padding-left: 10px; 17 | color: #373737; 18 | border-right: 1px solid lightgray; 19 | 20 | > span { 21 | white-space: nowrap; 22 | overflow: hidden; 23 | text-overflow: ellipsis; 24 | } 25 | } 26 | 27 | div.tab.selected, 28 | div.tab:hover { 29 | background-color: whitesmoke; 30 | } 31 | } 32 | 33 | 34 | // Scrollbar properties. 35 | div.tabs::-webkit-scrollbar { 36 | height: 6px; 37 | } 38 | 39 | div.tabs::-webkit-scrollbar-track { 40 | background: #f1f1f1; 41 | } 42 | 43 | div.tabs::-webkit-scrollbar-thumb { 44 | background: mat-color($app-primary, .7); 45 | } 46 | 47 | div.tabs::-webkit-scrollbar-thumb:hover { 48 | background: mat-color($app-primary, .8); 49 | } 50 | -------------------------------------------------------------------------------- /src/app/modules/application/components/tabs/tabs.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, ComponentFixture, TestBed} from '@angular/core/testing' 2 | 3 | import {TabsComponent} from './tabs.component' 4 | 5 | describe('TabsComponent', () => { 6 | let component: TabsComponent 7 | let fixture: ComponentFixture 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [TabsComponent] 12 | }) 13 | .compileComponents() 14 | })) 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TabsComponent) 18 | component = fixture.componentInstance 19 | fixture.detectChanges() 20 | }) 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/app/modules/application/components/tabs/tabs.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {MapCacheService} from '../../../../core/services/map-cache/map-cache.service' 3 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 4 | import {UtilsService} from '../../../../core/services/utils/utils.service' 5 | import {CachedMapEntry} from '../../../../shared/models/cached-map.model' 6 | 7 | @Component({ 8 | selector: 'mindmapp-tabs', 9 | templateUrl: './tabs.component.html', 10 | styleUrls: ['./tabs.component.scss'] 11 | }) 12 | export class TabsComponent implements OnInit { 13 | 14 | public cachedMapEntries: CachedMapEntry[] 15 | public attachedMap: CachedMapEntry 16 | 17 | constructor (private mapCacheService: MapCacheService, 18 | private mmpService: MmpService, 19 | private utilsService: UtilsService) { 20 | } 21 | 22 | public async ngOnInit () { 23 | this.cachedMapEntries = (await this.mapCacheService.getCachedMapEntries()) 24 | .sort(function (a: CachedMapEntry, b: CachedMapEntry) { 25 | return b.cachedMap.lastModified - a.cachedMap.lastModified 26 | }) 27 | 28 | this.mapCacheService.attachedMap.subscribe((attachedMap: CachedMapEntry) => { 29 | this.attachedMap = attachedMap 30 | 31 | for (const cachedMapEntry of this.cachedMapEntries) { 32 | if (cachedMapEntry.key === attachedMap.key) { 33 | cachedMapEntry.cachedMap = attachedMap.cachedMap 34 | return 35 | } 36 | } 37 | 38 | this.cachedMapEntries.unshift(attachedMap) 39 | }) 40 | } 41 | 42 | public openCachedMap (cachedMapEntry: CachedMapEntry) { 43 | this.mmpService.new(cachedMapEntry.cachedMap.data) 44 | this.mapCacheService.attachMap(cachedMapEntry) 45 | } 46 | 47 | public async removeCachedMap (key: string, event: MouseEvent) { 48 | event.stopPropagation() 49 | 50 | const confirmed = await this.utilsService.confirmDialog('MESSAGES.MAP_DELETION_CONFIRM') 51 | 52 | if (!confirmed) { 53 | return 54 | } 55 | 56 | await this.mapCacheService.removeCachedMap(key) 57 | 58 | // Remove the entry from the array. 59 | this.cachedMapEntries.splice(this.cachedMapEntries.findIndex((cachedMapEntry: CachedMapEntry) => { 60 | return cachedMapEntry.key === key 61 | }), 1) 62 | 63 | if (this.attachedMap.key === key) { 64 | // Select the last cached map. 65 | this.mmpService.new(this.cachedMapEntries[0].cachedMap.data) 66 | this.mapCacheService.attachMap(this.cachedMapEntries[0]) 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/app/modules/application/components/toolbar/toolbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | 19 | 23 | 27 | 31 | 32 | 33 |
34 |
35 |
36 | 37 | 41 | 45 | 49 | 50 |
51 |
52 |
53 | 54 | 58 | 62 | 63 |
64 |
65 |
66 | 67 | 71 | 75 | 76 | 77 | 78 | 82 | 83 |
84 |
85 |
86 | 87 | 90 | 91 | 94 | 95 | 98 |
99 | -------------------------------------------------------------------------------- /src/app/modules/application/components/toolbar/toolbar.component.scss: -------------------------------------------------------------------------------- 1 | @import "~src/theme.scss"; 2 | 3 | mat-toolbar { 4 | overflow-x: auto; 5 | height: 60px; 6 | padding: 0 8px; 7 | border-bottom: 1px solid lightgray; 8 | background-color: white !important; 9 | 10 | div.vertical-line { 11 | height: 100%; 12 | margin: 0 10px; 13 | 14 | > div { 15 | width: 1px; 16 | background-color: #c2c2c2; 17 | height: 100%; 18 | } 19 | } 20 | 21 | span.toolbar-spacer { 22 | flex: 1 1 auto; 23 | min-width: 50px; 24 | } 25 | } 26 | 27 | // Scrollbar properties. 28 | mat-toolbar::-webkit-scrollbar { 29 | height: 6px; 30 | } 31 | 32 | mat-toolbar::-webkit-scrollbar-track { 33 | background: #f1f1f1; 34 | } 35 | 36 | mat-toolbar::-webkit-scrollbar-thumb { 37 | background: mat-color($app-primary, .7); 38 | } 39 | 40 | mat-toolbar::-webkit-scrollbar-thumb:hover { 41 | background: mat-color($app-primary, .8); 42 | } 43 | -------------------------------------------------------------------------------- /src/app/modules/application/components/toolbar/toolbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, ComponentFixture, TestBed} from '@angular/core/testing' 2 | 3 | import {ToolbarComponent} from './toolbar.component' 4 | 5 | describe('ApplicationHeaderComponent', () => { 6 | let component: ToolbarComponent 7 | let fixture: ComponentFixture 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ToolbarComponent] 12 | }) 13 | .compileComponents() 14 | })) 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ToolbarComponent) 18 | component = fixture.componentInstance 19 | fixture.detectChanges() 20 | }) 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/app/modules/application/components/toolbar/toolbar.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input} from '@angular/core' 2 | import {MapCacheService} from '../../../../core/services/map-cache/map-cache.service' 3 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 4 | import {UtilsService} from '../../../../core/services/utils/utils.service' 5 | 6 | @Component({ 7 | selector: 'mindmapp-toolbar', 8 | templateUrl: './toolbar.component.html', 9 | styleUrls: ['./toolbar.component.scss'] 10 | }) 11 | export class ToolbarComponent { 12 | 13 | @Input() public node: any 14 | 15 | constructor (public mapCacheService: MapCacheService, 16 | public mmpService: MmpService) { 17 | } 18 | 19 | public createNewMap () { 20 | this.mmpService.new() 21 | this.mapCacheService.attachNewMap() 22 | } 23 | 24 | public exportMap (format: string) { 25 | this.mmpService.exportMap(format) 26 | } 27 | 28 | public toggleFullScreen () { 29 | UtilsService.toggleFullScreen() 30 | } 31 | 32 | public toogleNodeFontStyle () { 33 | const currentStyle = this.mmpService.selectNode().font.style 34 | 35 | if (currentStyle === 'italic') { 36 | this.mmpService.updateNode('fontStyle', 'normal') 37 | } else { 38 | this.mmpService.updateNode('fontStyle', 'italic') 39 | } 40 | } 41 | 42 | public toogleNodeFontWeight () { 43 | const currentWeight = this.mmpService.selectNode().font.weight 44 | 45 | if (currentWeight === 'bold') { 46 | this.mmpService.updateNode('fontWeight', 'normal') 47 | } else { 48 | this.mmpService.updateNode('fontWeight', 'bold') 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/application/application.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/application/application.component.scss: -------------------------------------------------------------------------------- 1 | div.mindmapp-application { 2 | height: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | 6 | mindmapp-map { 7 | flex: 1; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/application/application.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {MapOptions} from '../../../../shared/models/mmp.model' 3 | import {MapCacheService} from '../../../../core/services/map-cache/map-cache.service' 4 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 5 | import {SettingsService} from '../../../../core/services/settings/settings.service' 6 | import {UtilsService} from '../../../../core/services/utils/utils.service' 7 | import {NotificationService} from '../../../../core/services/notification/notification.service' 8 | 9 | @Component({ 10 | selector: 'mindmapp-application', 11 | templateUrl: './application.component.html', 12 | styleUrls: ['./application.component.scss'] 13 | }) 14 | export class ApplicationComponent implements OnInit { 15 | 16 | public node: any 17 | 18 | constructor (private mmpService: MmpService, 19 | private settingsService: SettingsService, 20 | private notificationService: NotificationService, 21 | private mapCacheService: MapCacheService) { 22 | this.node = {} 23 | } 24 | 25 | public async ngOnInit () { 26 | const settings = this.settingsService.getCachedSettings() 27 | 28 | // Create the mind map. 29 | this.initMap(settings.mapOptions) 30 | 31 | this.notificationService.setMessage('MESSAGES.INITIAL_INFORMATION') 32 | 33 | this.handleImageDropObservable() 34 | } 35 | 36 | public handleImageDropObservable () { 37 | UtilsService.observableDroppedImages().subscribe((image: string) => { 38 | this.mmpService.updateNode('imageSrc', image) 39 | }) 40 | } 41 | 42 | public async initMap (options: MapOptions) { 43 | this.mmpService.create('map_1', options) 44 | 45 | await this.mapCacheService.init() 46 | 47 | this.node = this.mmpService.selectNode() 48 | 49 | // Initialize all listeners 50 | this.createMapListeners() 51 | } 52 | 53 | public createMapListeners () { 54 | this.mmpService.on('create').subscribe(() => { 55 | Object.assign(this.node, this.mmpService.selectNode()) 56 | }) 57 | 58 | this.mmpService.on('nodeSelect').subscribe((node) => { 59 | Object.assign(this.node, node) 60 | }) 61 | 62 | this.mmpService.on('nodeDeselect').subscribe(() => { 63 | Object.assign(this.node, this.mmpService.selectNode()) 64 | }) 65 | 66 | this.mmpService.on('nodeUpdate').subscribe((node) => { 67 | Object.assign(this.node, node) 68 | this.mapCacheService.updateAttachedMap() 69 | }) 70 | 71 | this.mmpService.on('undo').subscribe(() => { 72 | Object.assign(this.node, this.mmpService.selectNode()) 73 | this.mapCacheService.updateAttachedMap() 74 | }) 75 | 76 | this.mmpService.on('redo').subscribe(() => { 77 | Object.assign(this.node, this.mmpService.selectNode()) 78 | this.mapCacheService.updateAttachedMap() 79 | }) 80 | 81 | this.mmpService.on('nodeCreate').subscribe(() => { 82 | this.mapCacheService.updateAttachedMap() 83 | }) 84 | 85 | this.mmpService.on('nodeRemove').subscribe(() => { 86 | this.mapCacheService.updateAttachedMap() 87 | }) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/settings/settings.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

{{ "PAGES.SETTINGS.TITLE" | translate }}

5 | 9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 | 17 | 18 | {{ "GENERAL.MAIN" | translate }} 19 | 20 |
21 | 22 | 23 | 26 | 28 | {{ "LANGUAGES." + language.toUpperCase() | translate }} 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | 37 | 38 |
39 | 40 | 41 | {{ "GENERAL.MAP" | translate }} 42 | 43 |
44 | 45 | 50 | {{ "GENERAL.CENTER_ON_RESIZING" | translate }} 51 | 52 | 57 | {{ "GENERAL.AUTO_BRANCH_COLORS" | translate }} 58 | 59 | 60 |
61 | 62 | 63 | {{ "GENERAL.NODES" | translate }} 64 | 65 |
66 | 67 | 68 | 73 | 74 | 75 | 80 | 81 | 82 |
83 |
84 |
85 |
86 |
87 |
88 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/settings/settings.component.scss: -------------------------------------------------------------------------------- 1 | div.settings { 2 | mat-toolbar { 3 | background-color: transparent; 4 | 5 | div.container { 6 | width: 100%; 7 | text-align: center; 8 | position: relative; 9 | 10 | button.close-button { 11 | position: absolute; 12 | top: -5px; 13 | right: -5px; 14 | } 15 | } 16 | } 17 | 18 | div.content { 19 | mat-card { 20 | margin-bottom: 20px; 21 | 22 | hr { 23 | margin-top: 0; 24 | margin-bottom: 20px; 25 | border: 0; 26 | height: 1px; 27 | background-color: lightgray; 28 | } 29 | } 30 | 31 | mat-card:last-child { 32 | margin-bottom: 0; 33 | } 34 | 35 | div.general-options { 36 | mat-card.general-options-main { 37 | padding-bottom: 15px; 38 | } 39 | } 40 | 41 | div.map-options { 42 | mat-card.map-options-map { 43 | mat-slide-toggle { 44 | display: block; 45 | margin-bottom: 20px; 46 | } 47 | 48 | mat-slide-toggle:last-child { 49 | margin-bottom: 0; 50 | } 51 | } 52 | 53 | mat-card.map-options-nodes { 54 | padding-bottom: 15px; 55 | 56 | mat-form-field { 57 | margin-right: 20px; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/settings/settings.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core' 2 | import {Settings} from '../../../../shared/models/settings.model' 3 | import {SettingsService} from '../../../../core/services/settings/settings.service' 4 | import {MmpService} from '../../../../core/services/mmp/mmp.service' 5 | import {TranslateService} from '@ngx-translate/core' 6 | 7 | @Component({ 8 | selector: 'mindmapp-settings', 9 | templateUrl: './settings.component.html', 10 | styleUrls: ['./settings.component.scss'] 11 | }) 12 | export class SettingsComponent { 13 | 14 | public readonly languages: string[] 15 | public settings: Settings 16 | 17 | constructor (private settingsService: SettingsService, 18 | private mmpService: MmpService, 19 | private translateService: TranslateService) { 20 | this.languages = SettingsService.LANGUAGES 21 | this.settings = this.settingsService.getCachedSettings() 22 | } 23 | 24 | public async updateMapOptions () { 25 | await this.settingsService.updateCachedSettings(this.settings) 26 | 27 | this.mmpService.updateOptions('rootNode', this.settings.mapOptions.rootNode) 28 | this.mmpService.updateOptions('defaultNode', this.settings.mapOptions.defaultNode) 29 | this.mmpService.updateOptions('centerOnResize', this.settings.mapOptions.centerOnResize) 30 | 31 | } 32 | 33 | public async updateLanguage () { 34 | await this.settingsService.updateCachedSettings(this.settings) 35 | 36 | this.translateService.use(this.settings.general.language) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/shortcuts/shortcuts.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

{{ "PAGES.SHORTCUTS.TITLE" | translate }}

5 | 9 |
10 |
11 | 12 |
13 | 14 | 15 | 16 | {{ key }} +  17 | 18 | 19 | {{ shortcut.description | translate }} 20 | 21 | 22 | 23 | 24 | 25 | 26 | {{ key }} +  27 | 28 | 29 | {{ shortcut.description | translate }} 30 | 31 | 32 | 33 |
34 |
35 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/shortcuts/shortcuts.component.scss: -------------------------------------------------------------------------------- 1 | div.shortcuts { 2 | mat-toolbar { 3 | background-color: transparent; 4 | 5 | div.container { 6 | width: 100%; 7 | text-align: center; 8 | position: relative; 9 | 10 | button.close-button { 11 | position: absolute; 12 | top: -5px; 13 | right: -5px; 14 | } 15 | } 16 | } 17 | 18 | div.content { 19 | display: flex; 20 | justify-content: space-around; 21 | flex-wrap: wrap; 22 | padding-bottom: 10px; 23 | 24 | mat-list-item { 25 | color: #555555; 26 | } 27 | 28 | .key { 29 | padding: 6px 12px; 30 | background: gainsboro; 31 | border-radius: 3px; 32 | font-size: 15px; 33 | } 34 | 35 | .description { 36 | padding-left: 10px; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/modules/application/pages/shortcuts/shortcuts.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {ShortcutsService} from '../../../../core/services/shortcuts/shortcuts.service' 3 | import {Hotkey} from 'angular2-hotkeys' 4 | 5 | @Component({ 6 | selector: 'mindmapp-shortcuts', 7 | templateUrl: './shortcuts.component.html', 8 | styleUrls: ['./shortcuts.component.scss'] 9 | }) 10 | export class ShortcutsComponent implements OnInit { 11 | 12 | public shortcuts: any[] 13 | 14 | constructor (private shortcutsService: ShortcutsService) { 15 | } 16 | 17 | public ngOnInit () { 18 | const hotKeys: Hotkey[] = this.shortcutsService.getHotKeys() 19 | this.shortcuts = hotKeys.map((hotKey: Hotkey) => { 20 | const keys = hotKey.combo[0] 21 | 22 | return { 23 | keys: keys === '+' ? [keys] : keys.split('+'), 24 | description: hotKey.description 25 | } 26 | }) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/app/root-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core' 2 | import {RouterModule, Routes} from '@angular/router' 3 | 4 | const routes: Routes = [{ 5 | path: '', 6 | loadChildren: () => import('./modules/about/about.module').then(m => m.AboutModule) 7 | }, { 8 | path: 'app', 9 | loadChildren: () => import('./modules/application/application.module').then(m => m.ApplicationModule) 10 | }, { 11 | path: '**', 12 | redirectTo: '' 13 | }] 14 | 15 | @NgModule({ 16 | imports: [RouterModule.forRoot(routes)], 17 | exports: [RouterModule] 18 | }) 19 | export class RootRoutingModule { 20 | } 21 | -------------------------------------------------------------------------------- /src/app/root.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/app/root.component.scss: -------------------------------------------------------------------------------- 1 | main { 2 | height: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/root.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core' 2 | import {ShortcutsService} from './core/services/shortcuts/shortcuts.service' 3 | import {TranslateService} from '@ngx-translate/core' 4 | import {SettingsService} from './core/services/settings/settings.service' 5 | import {NotificationService} from './core/services/notification/notification.service' 6 | import {routeAnimation} from './shared/animations/route.animation' 7 | import {Router} from '@angular/router' 8 | 9 | @Component({ 10 | selector: 'mindmapp-root', 11 | templateUrl: 'root.component.html', 12 | styleUrls: ['./root.component.scss'], 13 | animations: [routeAnimation] 14 | }) 15 | export class RootComponent implements OnInit { 16 | 17 | constructor (private notificationService: NotificationService, 18 | private translateService: TranslateService, 19 | private router: Router, 20 | private settingsService: SettingsService, 21 | private shortcutsService: ShortcutsService) { 22 | } 23 | 24 | public async ngOnInit () { 25 | const {settings, isFirstTime} = await this.settingsService.init() 26 | 27 | if (isFirstTime) { 28 | settings.general.language = this.translateService.getBrowserLang() 29 | await this.settingsService.updateCachedSettings(settings) 30 | } 31 | 32 | await this.initTranslations(settings.general.language) 33 | 34 | this.shortcutsService.init() 35 | 36 | // If there is a PWA environment go to application page as default. 37 | if (window.matchMedia('(display-mode: standalone)').matches) { 38 | this.router.navigate(['app'], { 39 | replaceUrl: true 40 | }) 41 | } 42 | 43 | if (isFirstTime) { 44 | this.notificationService.showSnackBarMessage('MESSAGES.WELCOME_MESSAGE') 45 | } 46 | } 47 | 48 | private async initTranslations (language: string): Promise { 49 | this.translateService.setDefaultLang(language) 50 | await this.translateService.use(language).toPromise() 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/app/root.module.ts: -------------------------------------------------------------------------------- 1 | import {BrowserModule} from '@angular/platform-browser' 2 | import {NgModule} from '@angular/core' 3 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations' 4 | import {RootComponent} from './root.component' 5 | import {RootRoutingModule} from './root-routing.module' 6 | import {TranslateLoader, TranslateModule} from '@ngx-translate/core' 7 | import {HttpClient, HttpClientModule} from '@angular/common/http' 8 | import {TranslateHttpLoader} from '@ngx-translate/http-loader' 9 | import {SharedModule} from './shared/shared.module' 10 | import {HotkeyModule} from 'angular2-hotkeys' 11 | import {ServiceWorkerModule} from '@angular/service-worker' 12 | import {environment} from '../environments/environment' 13 | 14 | export function createTranslateLoader (http: HttpClient) { 15 | return new TranslateHttpLoader(http, './assets/i18n/', '.json') 16 | } 17 | 18 | @NgModule({ 19 | imports: [ 20 | BrowserModule, 21 | SharedModule, 22 | BrowserAnimationsModule, 23 | RootRoutingModule, 24 | HttpClientModule, 25 | TranslateModule.forRoot({ 26 | loader: { 27 | provide: TranslateLoader, 28 | useFactory: (createTranslateLoader), 29 | deps: [HttpClient] 30 | } 31 | }), 32 | HotkeyModule.forRoot(), 33 | ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}) 34 | ], 35 | declarations: [ 36 | RootComponent 37 | ], 38 | bootstrap: [RootComponent] 39 | }) 40 | export class RootModule { 41 | } 42 | -------------------------------------------------------------------------------- /src/app/shared/animations/route.animation.ts: -------------------------------------------------------------------------------- 1 | import {animate, query, style, transition, trigger} from '@angular/animations' 2 | 3 | export const routeAnimation = trigger('routeAnimation', [ 4 | transition('* => *', [ 5 | // Set a default style for enter and leave 6 | query(':enter, :leave', [ 7 | style({ 8 | position: 'absolute', 9 | width: '100%', 10 | height: '100%', 11 | opacity: 0 12 | }), 13 | ], { 14 | optional: true 15 | }), 16 | // Animate the new page in 17 | query(':enter', [ 18 | animate('600ms ease', style({ 19 | opacity: 1 20 | })), 21 | ], { 22 | optional: true 23 | }) 24 | ]), 25 | ]) 26 | -------------------------------------------------------------------------------- /src/app/shared/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/app/shared/components/.gitkeep -------------------------------------------------------------------------------- /src/app/shared/models/cached-map.model.ts: -------------------------------------------------------------------------------- 1 | export interface CachedMapEntry { 2 | cachedMap: CachedMap, 3 | key: string 4 | } 5 | 6 | export interface CachedMap { 7 | lastModified: number 8 | data: any 9 | } 10 | -------------------------------------------------------------------------------- /src/app/shared/models/mmp.model.ts: -------------------------------------------------------------------------------- 1 | export interface MapOptions { 2 | centerOnResize: boolean 3 | autoBranchColors: boolean 4 | defaultNode: DefaultNode 5 | rootNode: RootNode 6 | } 7 | 8 | interface DefaultNode { 9 | name: string 10 | image: Image 11 | colors: DefaultNodeColors 12 | font: Font 13 | locked: boolean 14 | } 15 | 16 | interface RootNode { 17 | name: string 18 | image: Image 19 | colors: RootNodeColors 20 | font: Font 21 | } 22 | 23 | interface Image { 24 | src: string 25 | size: number 26 | } 27 | 28 | interface DefaultNodeColors { 29 | name: string 30 | background: string 31 | branch: string 32 | } 33 | 34 | interface RootNodeColors { 35 | name: string 36 | background: string 37 | } 38 | 39 | interface Font { 40 | size: number 41 | style: string 42 | weight: string 43 | } 44 | -------------------------------------------------------------------------------- /src/app/shared/models/settings.model.ts: -------------------------------------------------------------------------------- 1 | import {MapOptions} from './mmp.model' 2 | 3 | export interface Settings { 4 | general: General 5 | mapOptions: MapOptions 6 | } 7 | 8 | interface General { 9 | language: string 10 | } 11 | -------------------------------------------------------------------------------- /src/app/shared/pipes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/app/shared/pipes/.gitkeep -------------------------------------------------------------------------------- /src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import {TranslateModule} from '@ngx-translate/core' 2 | import {CommonModule} from '@angular/common' 3 | import {NgModule} from '@angular/core' 4 | import {FormsModule} from '@angular/forms' 5 | import {ColorPickerModule} from 'ngx-color-picker' 6 | import {RouterModule} from '@angular/router' 7 | import {FontAwesomeModule} from '@fortawesome/angular-fontawesome' 8 | import {MatSliderModule} from '@angular/material/slider' 9 | import {MatIconModule} from '@angular/material/icon' 10 | import {MatSnackBarModule} from '@angular/material/snack-bar' 11 | import {MatButtonModule} from '@angular/material/button' 12 | import {MatCardModule} from '@angular/material/card' 13 | import {MatListModule} from '@angular/material/list' 14 | import {MatToolbarModule} from '@angular/material/toolbar' 15 | import {MatSelectModule} from '@angular/material/select' 16 | 17 | @NgModule({ 18 | imports: [ 19 | CommonModule, 20 | FormsModule, 21 | FontAwesomeModule, 22 | MatSliderModule, 23 | RouterModule, 24 | TranslateModule, 25 | MatIconModule, 26 | MatSnackBarModule, 27 | ColorPickerModule, 28 | MatButtonModule, 29 | MatCardModule, 30 | MatListModule, 31 | MatToolbarModule 32 | ], 33 | exports: [ 34 | CommonModule, 35 | FormsModule, 36 | FontAwesomeModule, 37 | MatSliderModule, 38 | TranslateModule, 39 | MatIconModule, 40 | MatSelectModule, 41 | MatSnackBarModule, 42 | ColorPickerModule, 43 | MatButtonModule, 44 | MatCardModule, 45 | MatListModule, 46 | MatToolbarModule 47 | ] 48 | }) 49 | export class SharedModule { 50 | } 51 | -------------------------------------------------------------------------------- /src/assets/data/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "language": "en" 4 | }, 5 | "mapOptions": { 6 | "centerOnResize": false, 7 | "autoBranchColors": false, 8 | "defaultNode": { 9 | "name": "Node", 10 | "image": { 11 | "src": "", 12 | "size": 60 13 | }, 14 | "colors": { 15 | "name": "#666666", 16 | "background": "#f5f5f5", 17 | "branch": "#546e7a" 18 | }, 19 | "font": { 20 | "size": 22, 21 | "style": "normal", 22 | "weight": "normal" 23 | }, 24 | "locked": true 25 | }, 26 | "rootNode": { 27 | "name": "Root node", 28 | "image": { 29 | "src": "", 30 | "size": 70 31 | }, 32 | "colors": { 33 | "name": "#666666", 34 | "background": "#f5f5f5" 35 | }, 36 | "font": { 37 | "size": 26, 38 | "style": "normal", 39 | "weight": "normal" 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-Black.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-BlackItalic.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-Bold.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-ExtraLight.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-Italic.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-Light.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/font/source-sans-pro/SourceSansPro-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/font/source-sans-pro/SourceSansPro-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "Englisch", 4 | "FR": "Französisch", 5 | "IT": "Italienisch", 6 | "ZH-TW": "Traditionelles Chinesisch", 7 | "ZH-CN": "Vereinfachtes Chinesisch", 8 | "PT-BR": "Portugiesisch (Brasilien", 9 | "ES": "Spanisch", 10 | "DE": "Deutsch" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Mindmap importieren", 14 | "EXPORT_MAP": "Mindmap exportieren", 15 | "ESC": "Dialog schließen", 16 | "FULLSCREEN": "Vollbild", 17 | "CLOSE_MENU": "Seitenmenü öffnen", 18 | "OPEN_MENU": "Seitenmenü schließen", 19 | "SHORTCUTS": "Shortcuts anzeigen", 20 | "MENU": "Menü öffnen", 21 | "NEW_MAP": "Neue Mindmap", 22 | "OPEN_MAP": "Mindmap öffnen", 23 | "SAVE_MAP": "Mindmap speichern", 24 | "SAVE_MAP_WITH_NAME": "Mindmap speichern unter...", 25 | "COPY_NODE": "Aktuellen Knoten kopieren", 26 | "CUT_NODE": "Aktuellen Knoten ausschneiden", 27 | "PASTE_NODE": "Kopierten Knoten einfügen", 28 | "MOVE_NODE_TO_THE_LEFT": "Knoten nach links verschieben", 29 | "MOVE_NODE_TO_THE_RIGHT": "Knoten nach rechts verschieben", 30 | "MOVE_NODE_DOWN": "Knoten nach unten verschieben", 31 | "MOVE_NODE_UPWARD": "Knoten nach oben verschieben", 32 | "SELECT_NODE_ON_THE_LEFT": "Linken Knoten auswählen", 33 | "SELECT_NODE_ON_THE_RIGHT": "Rechten Knoten auswählen", 34 | "SELECT_NODE_BELOW": "Knoten unten auswählen", 35 | "SELECT_NODE_ABOVE": "Knoten oben auswählen", 36 | "EDIT_NODE": "Änderungen am Text eines Knotens erlauben", 37 | "LOCK_NODE": "Knoten koppeln oder entkoppeln", 38 | "NODE_IMAGE": "Bild zum Knoten hinzufügen oder entfernen", 39 | "BOLD_TEXT": "Fett", 40 | "ITALIC_TEXT": "Kursiv", 41 | "TEXT_SIZE": "Textgröße", 42 | "IMAGE_SIZE": "Bildgröße", 43 | "ADD_NODE": "Knoten hinzufügen", 44 | "REMOVE_NODE": "Knoten löschen", 45 | "ZOOM_IN_MAP": "Mindmap vergrößern", 46 | "ZOOM_OUT_MAP": "Mindmap verkleinern", 47 | "CENTER_MAP": "Mindmap zentrieren", 48 | "UNDO_MAP": "Letzte Änderung zurücknehmen", 49 | "REDO_MAP": "Letzte Änderung wiederherstellen", 50 | "NODE_COLOR": "Knotenfarbe ändern", 51 | "BRANCH_COLOR": "Zweigfarbe ändern", 52 | "TEXT_COLOR": "Textfarbe ändern", 53 | "ROOT_NODE_NAME_INPUT": "Standardbezeichnung des Wurzelknotens ändern", 54 | "NODE_NAME_INPUT": "Standardbezeichnung von Knoten ändern", 55 | "LANGUAGE": "Sprache ändern", 56 | "SETTINGS": "Einstellungen", 57 | "CENTER_ON_RESIZING": "Mindmap bei Änderung der Fenstergröße zentrieren", 58 | "NODE_DRAGGING": "Verschieben von Knoten erlauben", 59 | "MAP_ZOOM": "Zoomen der Mindmap erlauben", 60 | "AUTO_BRANCH_COLORS": "Automatische Auswahl von Zweigfarben erlauben" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "Einstellungen" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "Shortcuts" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "Keine Mindmap vorhanden!", 125 | "INITIAL_INFORMATION": "»Mindmapp« ist bereit ☺", 126 | "WELCOME_MESSAGE": "Willkommen zur Anwendung »Mindmapp« ☺", 127 | "MAP_DELETION_CONFIRM": "Bist du sicher, dass du die Mindmap löschen möchtest?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organisation", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Anwendung", 134 | "TRY_IT": "Ausprobieren", 135 | "GENERAL": "Allgemein", 136 | "MAP_OPTIONS": "Mindmap-Optionen", 137 | "DISMISS": "Verwerfen", 138 | "SAVED_MAPS": "Gespeicherte Mindmaps", 139 | "JSON": "Datei (.json)", 140 | "PNG": "Bild (.png)", 141 | "JPG": "Bild (.jpg)", 142 | "PDF": "Dokument (.pdf)", 143 | "LANGUAGE": "Sprache", 144 | "YES": "Ja", 145 | "NO": "Nein", 146 | "OK": "Ok", 147 | "SAVE": "Speichern", 148 | "OPEN": "Öffnen", 149 | "CANCEL": "Abbrechen", 150 | "MAIN": "Grundeinstellungen", 151 | "CENTER_ON_RESIZING": "Zentrieren bei Größenänderung", 152 | "NODE_DRAGGING": "Verschieben von Knoten", 153 | "MAP_ZOOM": "Zoom", 154 | "AUTO_BRANCH_COLORS": "Automatische Zweigfarben", 155 | "MAP": "Mindmap", 156 | "NODES": "Knoten", 157 | "SAVED": "Gespeichert", 158 | "NOT_SAVED": "Nicht gespeichert", 159 | "ROOT_NODE_NAME": "Bezeichnung des Wurzelknotens", 160 | "NODE_NAME": "Bezeichnung von Knoten" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "English", 4 | "FR": "French", 5 | "IT": "Italian", 6 | "ZH-TW": "Traditional Chinese", 7 | "ZH-CN": "Simplified Chinese", 8 | "PT-BR": "Portuguese Brazil", 9 | "ES": "Spanish", 10 | "DE": "German" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Imports the map", 14 | "EXPORT_MAP": "Exports the map", 15 | "ESC": "Closes the dialog window", 16 | "FULLSCREEN": "Sets the full screen", 17 | "CLOSE_MENU": "Closes the side menu", 18 | "OPEN_MENU": "Opens the side menu", 19 | "SHORTCUTS": "Shows the shortcuts", 20 | "MENU": "Opens the menu", 21 | "NEW_MAP": "Cleans the map", 22 | "OPEN_MAP": "Loads an existing map", 23 | "SAVE_MAP": "Saves the map", 24 | "SAVE_MAP_WITH_NAME": "Saves the map with name", 25 | "COPY_NODE": "Copies the current node", 26 | "CUT_NODE": "Cuts the current node", 27 | "PASTE_NODE": "Paste the copied node", 28 | "MOVE_NODE_TO_THE_LEFT": "Moves the node to the left", 29 | "MOVE_NODE_TO_THE_RIGHT": "Moves the node to the right", 30 | "MOVE_NODE_DOWN": "Moves the node down", 31 | "MOVE_NODE_UPWARD": "Moves the node upward", 32 | "SELECT_NODE_ON_THE_LEFT": "Selects the node on the left", 33 | "SELECT_NODE_ON_THE_RIGHT": "Selects the node on the right", 34 | "SELECT_NODE_BELOW": "Selects the node below", 35 | "SELECT_NODE_ABOVE": "Selects the node above", 36 | "EDIT_NODE": "Allows to edit the text of the node", 37 | "LOCK_NODE": "Locks or unlocks the node", 38 | "NODE_IMAGE": "Adds an image to the node", 39 | "BOLD_TEXT": "Changes the weight of the text", 40 | "ITALIC_TEXT": "Changes the style of the text", 41 | "TEXT_SIZE": "Changes the size of the text and the node", 42 | "IMAGE_SIZE": "Changes the size of the node image", 43 | "ADD_NODE": "Adds a node", 44 | "REMOVE_NODE": "Removes a node", 45 | "ZOOM_IN_MAP": "Zooms in the map", 46 | "ZOOM_OUT_MAP": "Zooms out the map", 47 | "CENTER_MAP": "Centers the map", 48 | "UNDO_MAP": "Undoes the last change", 49 | "REDO_MAP": "Repeats a previously undoed change", 50 | "NODE_COLOR": "Changes the node color", 51 | "BRANCH_COLOR": "Changes the branch color", 52 | "TEXT_COLOR": "Changes the text color", 53 | "ROOT_NODE_NAME_INPUT": "Changes the default root node name", 54 | "NODE_NAME_INPUT": "Changes the default node name", 55 | "LANGUAGE": "Changes the language", 56 | "SETTINGS": "Opens the settings", 57 | "CENTER_ON_RESIZING": "Centers the map on window resizing", 58 | "NODE_DRAGGING": "Allows to drag the nodes in the map", 59 | "MAP_ZOOM": "Allows to zoom the map", 60 | "AUTO_BRANCH_COLORS": "Allows to choose automatically the colors of the branches" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "Settings" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "Shortcuts" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "No saved maps!", 125 | "INITIAL_INFORMATION": "Mindmapp is ready ☺", 126 | "WELCOME_MESSAGE": "Welcome to the Mindmapp app ☺", 127 | "MAP_DELETION_CONFIRM": "Are you sure you want to delete the map?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organization", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Application", 134 | "TRY_IT": "Try it", 135 | "GENERAL": "General", 136 | "MAP_OPTIONS": "Map options", 137 | "DISMISS": "Dismiss", 138 | "SAVED_MAPS": "Saved maps", 139 | "JSON": "File (.json)", 140 | "PNG": "Image (.png)", 141 | "JPG": "Image (.jpg)", 142 | "PDF": "Document (.pdf)", 143 | "LANGUAGE": "Language", 144 | "YES": "Yes", 145 | "NO": "No", 146 | "OK": "Ok", 147 | "SAVE": "Save", 148 | "OPEN": "Open", 149 | "CANCEL": "Cancel", 150 | "MAIN": "Main", 151 | "CENTER_ON_RESIZING": "Center on resizing", 152 | "NODE_DRAGGING": "Node dragging", 153 | "MAP_ZOOM": "Map zoom", 154 | "AUTO_BRANCH_COLORS": "Automatic branch colors", 155 | "MAP": "Map", 156 | "NODES": "Nodes", 157 | "SAVED": "Saved", 158 | "NOT_SAVED": "Not saved", 159 | "ROOT_NODE_NAME": "Root node name", 160 | "NODE_NAME": "Node name" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "Inglés", 4 | "FR": "Francés", 5 | "IT": "Italiano", 6 | "ZH-TW": "Chino tradicional", 7 | "ZH-CN": "chino simplificado", 8 | "PT-BR": "Portugués Brasil", 9 | "ES": "Español", 10 | "DE": "Alemán" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Imports the map", 14 | "EXPORT_MAP": "Exports the map", 15 | "ESC": "Cerrar el cuadro de diálogo", 16 | "FULLSCREEN": "A pantalla completa", 17 | "CLOSE_MENU": "Cerrar el menú lateral", 18 | "OPEN_MENU": "Abrir el menú lateral", 19 | "SHORTCUTS": "Mostrar los atajos", 20 | "MENU": "Abrir el menú", 21 | "NEW_MAP": "Limpiar el mapa", 22 | "OPEN_MAP": "Cargar un mapa existente", 23 | "SAVE_MAP": "Guardar el mapa", 24 | "SAVE_MAP_WITH_NAME": "Guardar el mapa bajo un nombre", 25 | "COPY_NODE": "Copiar el nodo actual", 26 | "CUT_NODE": "Cortar el nodo actual", 27 | "PASTE_NODE": "Pegar el nodo copiado", 28 | "MOVE_NODE_TO_THE_LEFT": "Mover el nodo a la izquierda", 29 | "MOVE_NODE_TO_THE_RIGHT": "Mover el nodo a la derecha", 30 | "MOVE_NODE_DOWN": "Mover el nodo hacia abajo", 31 | "MOVE_NODE_UPWARD": "Mover el nodo hacia arriba", 32 | "SELECT_NODE_ON_THE_LEFT": "Seleccionar el nodo de la izquierda", 33 | "SELECT_NODE_ON_THE_RIGHT": "Seleccionar el nodo de la derecha", 34 | "SELECT_NODE_BELOW": "Seleccionar el nodo de abajo", 35 | "SELECT_NODE_ABOVE": "Seleccionar el nodo de arriba", 36 | "EDIT_NODE": "Permitir editar el texto del nodo", 37 | "LOCK_NODE": "Bloquear o desbloquear el nodo", 38 | "NODE_IMAGE": "Añadir una imagen al nodo", 39 | "BOLD_TEXT": "Cambiar la importancia del texto", 40 | "ITALIC_TEXT": "Cambiar el estilo del texto", 41 | "TEXT_SIZE": "Cambiar el tamaño del texto y del nodo", 42 | "IMAGE_SIZE": "Cambiar el tamaño de la imagen del nodo", 43 | "ADD_NODE": "Agregar un nodo", 44 | "REMOVE_NODE": "Quitar un nodo", 45 | "ZOOM_IN_MAP": "Acercar el mapa", 46 | "ZOOM_OUT_MAP": "Alejar el mapa", 47 | "CENTER_MAP": "Centrar el mapa", 48 | "UNDO_MAP": "Deshacer el último cambio", 49 | "REDO_MAP": "Repetir un cambio previamente deshecho", 50 | "NODE_COLOR": "Cambiar el color del nodo", 51 | "BRANCH_COLOR": "Cambiar el color de la rama", 52 | "TEXT_COLOR": "Cambiar el color del texto", 53 | "ROOT_NODE_NAME_INPUT": "Cambiar el nombre predeterminado del nodo raíz", 54 | "NODE_NAME_INPUT": "Cambiar el nombre predeterminado del nodo", 55 | "LANGUAGE": "Cambiar el idioma", 56 | "SETTINGS": "Configurar los Ajustes", 57 | "CENTER_ON_RESIZING": "Centrar el mapa durante el redimensionamiento de la ventana", 58 | "NODE_DRAGGING": "Permitir arrastrar los nodos en el mapa", 59 | "MAP_ZOOM": "Permitir acercar el mapa", 60 | "AUTO_BRANCH_COLORS": "Permitir elegir automáticamente los colores de las ramificaciones" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "Ajustes" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "Atajos" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "No saved maps!", 125 | "INITIAL_INFORMATION": "Mindmapp está listo ☺", 126 | "WELCOME_MESSAGE": "Bienvenido a Mindmapp app ☺", 127 | "MAP_DELETION_CONFIRM": "Are you sure you want to delete the map?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organization", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Application", 134 | "TRY_IT": "Try it", 135 | "GENERAL": "General", 136 | "MAP_OPTIONS": "Opciones del mapa", 137 | "DISMISS": "Desestimar", 138 | "SAVED_MAPS": "Saved maps", 139 | "JSON": "File (.json)", 140 | "PNG": "Imagen (.png)", 141 | "JPG": "Imagen (.jpg)", 142 | "PDF": "Documento (.pdf)", 143 | "LANGUAGE": "Idioma", 144 | "YES": "Si", 145 | "NO": "No", 146 | "OK": "Ok", 147 | "SAVE": "Guardar", 148 | "OPEN": "Abrir", 149 | "CANCEL": "Cancelar", 150 | "MAIN": "Principal", 151 | "CENTER_ON_RESIZING": "Centrar cuando se redimensiona", 152 | "NODE_DRAGGING": "Arrastrar nodo", 153 | "MAP_ZOOM": "Zoom del mapa", 154 | "AUTO_BRANCH_COLORS": "Colores automáticos de las ramificaciones", 155 | "MAP": "Mapa", 156 | "NODES": "Nodos", 157 | "SAVED": "Guardado", 158 | "NOT_SAVED": "No guardado", 159 | "ROOT_NODE_NAME": "Nombre del nodo raíz", 160 | "NODE_NAME": "Nombre del nodo" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "Anglais", 4 | "FR": "Français", 5 | "IT": "Italien", 6 | "ZH-TW": "Chinois traditionnel", 7 | "ZH-CN": "Chinois simplifié", 8 | "PT-BR": "Portuguese Brazil", 9 | "ES": "Espagnol", 10 | "DE": "Allemand" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Imports the map", 14 | "EXPORT_MAP": "Exports the map", 15 | "ESC": "Ferme la fenêtre de dialogue", 16 | "FULLSCREEN": "Mettre en plein écran", 17 | "CLOSE_MENU": "Ferme le menu latéral", 18 | "OPEN_MENU": "Ouvre le menu latéral", 19 | "SHORTCUTS": "Affiche les raccourcis", 20 | "MENU": "Ouvre le menu", 21 | "NEW_MAP": "Nettoie la carte", 22 | "OPEN_MAP": "Charge une carte existante", 23 | "SAVE_MAP": "Enregistre la carte", 24 | "SAVE_MAP_WITH_NAME": "Enregistre la carte avec son nom", 25 | "COPY_NODE": "Copie le nœud actuel", 26 | "CUT_NODE": "Coupe le nœud actuel", 27 | "PASTE_NODE": "Collez le noeud copié", 28 | "MOVE_NODE_TO_THE_LEFT": "Déplace le nœud vers la gauche", 29 | "MOVE_NODE_TO_THE_RIGHT": "Déplace le noeud vers la droite", 30 | "MOVE_NODE_DOWN": "Déplace le nœud vers le bas", 31 | "MOVE_NODE_UPWARD": "Déplace le nœud vers le haut", 32 | "SELECT_NODE_ON_THE_LEFT": "Sélectionne le nœud à gauche", 33 | "SELECT_NODE_ON_THE_RIGHT": "Sélectionne le noeud à droite", 34 | "SELECT_NODE_BELOW": "Sélectionne le nœud ci-dessous", 35 | "SELECT_NODE_ABOVE": "Sélectionne le nœud ci-dessus", 36 | "EDIT_NODE": "Allows to edit the text of the node", 37 | "LOCK_NODE": "Verrouille ou déverrouille le nœud", 38 | "NODE_IMAGE": "Ajoute une image au noeud", 39 | "BOLD_TEXT": "Change la taille du texte", 40 | "ITALIC_TEXT": "Change le style du texte", 41 | "TEXT_SIZE": "Change la taille du texte et du noeud", 42 | "IMAGE_SIZE": "Change la taille de l'image du noeud", 43 | "ADD_NODE": "Ajoute un noeud", 44 | "REMOVE_NODE": "Supprime un noeud", 45 | "ZOOM_IN_MAP": "Zoom sur la carte", 46 | "ZOOM_OUT_MAP": "Dézoom la carte", 47 | "CENTER_MAP": "Centre la carte", 48 | "UNDO_MAP": "Défait le dernier changement", 49 | "REDO_MAP": "Répète un changement précédemment annulé", 50 | "NODE_COLOR": "Change la couleur du noeud", 51 | "BRANCH_COLOR": "Change la couleur de la branche", 52 | "TEXT_COLOR": "Change la couleur du texte", 53 | "ROOT_NODE_NAME_INPUT": "Modifie le nom du noeud racine par défaut", 54 | "NODE_NAME_INPUT": "Modifie le nom de noeud par défaut", 55 | "LANGUAGE": "Change la langue", 56 | "SETTINGS": "Ouvre les paramètres", 57 | "CENTER_ON_RESIZING": "Centre la carte sur le redimensionnement de la fenêtre", 58 | "NODE_DRAGGING": "Permet de faire glisser les nœuds dans la carte", 59 | "MAP_ZOOM": "Permet de zoomer sur la carte", 60 | "AUTO_BRANCH_COLORS": "Allows to choose automatically the colors of the branches" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "Paramètres" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "Raccourcis" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "No saved maps!", 125 | "INITIAL_INFORMATION": "Mindmapp est prêt ☺", 126 | "WELCOME_MESSAGE": "Welcome to the Mindmapp app ☺", 127 | "MAP_DELETION_CONFIRM": "Are you sure you want to delete the map?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organization", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Application", 134 | "TRY_IT": "Try it", 135 | "GENERAL": "Général", 136 | "MAP_OPTIONS": "Options de carte", 137 | "DISMISS": "Rejeter", 138 | "SAVED_MAPS": "Saved maps", 139 | "JSON": "File (.json)", 140 | "PNG": "Image (.png)", 141 | "JPG": "Image (.jpg)", 142 | "PDF": "Document (.pdf)", 143 | "LANGUAGE": "Language", 144 | "YES": "Oui", 145 | "NO": "Non", 146 | "OK": "Ok", 147 | "SAVE": "Save", 148 | "OPEN": "Open", 149 | "CANCEL": "Annuler", 150 | "MAIN": "Principale", 151 | "CENTER_ON_RESIZING": "Centrer sur le redimensionnement", 152 | "NODE_DRAGGING": "Noeud faisant glisser", 153 | "MAP_ZOOM": "Map zoom", 154 | "AUTO_BRANCH_COLORS": "Automatic branch colors", 155 | "MAP": "Carte", 156 | "NODES": "Noeuds", 157 | "SAVED": "Enregistré", 158 | "NOT_SAVED": "Non enregistré", 159 | "ROOT_NODE_NAME": "Nom du noeud racine", 160 | "NODE_NAME": "Nom du noeud" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "Inglese", 4 | "FR": "Francese", 5 | "IT": "Italiano", 6 | "ZH-TW": "Cinese tradizionale", 7 | "ZH-CN": "Cinese semplificato", 8 | "PT-BR": "Portoghese Brasile", 9 | "ES": "Spagnolo", 10 | "DE": "Tedesco" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Importa la mappa", 14 | "EXPORT_MAP": "Esporta la mappa", 15 | "ESC": "Chiude la finestra di dialogo", 16 | "FULLSCREEN": "Imposta lo schermo intero", 17 | "CLOSE_MENU": "Chiude il menu' laterale", 18 | "OPEN_MENU": "Apre il menu' laterale", 19 | "SHORTCUTS": "Mostra le scorciatoie da tastiera", 20 | "MENU": "Apre il menù", 21 | "NEW_MAP": "Pulisce la mappa", 22 | "OPEN_MAP": "Carica una mappa esistente", 23 | "SAVE_MAP": "Salva la mappa", 24 | "SAVE_MAP_WITH_NAME": "Salva la mappa con nome", 25 | "COPY_NODE": "Copia il nodo selezionato", 26 | "CUT_NODE": "Taglia il nodo selezionato", 27 | "PASTE_NODE": "Incolla il nodo copiato", 28 | "MOVE_NODE_TO_THE_LEFT": "Muove il nodo a sinistra", 29 | "MOVE_NODE_TO_THE_RIGHT": "Muove il nodo a destra", 30 | "MOVE_NODE_DOWN": "Muove il nodo in basso", 31 | "MOVE_NODE_UPWARD": "Muove il nodo in alto", 32 | "SELECT_NODE_ON_THE_LEFT": "Seleziona il nodo a sinistra", 33 | "SELECT_NODE_ON_THE_RIGHT": "Seleziona il nodo a destra", 34 | "SELECT_NODE_BELOW": "Seleziona il nodo in alto", 35 | "SELECT_NODE_ABOVE": "Seleziona il nodo in basso", 36 | "EDIT_NODE": "Permette di modificare il testo del nodo", 37 | "LOCK_NODE": "Blocca o sblocca il nodo", 38 | "NODE_IMAGE": "Aggiunge un'immagine al nodo", 39 | "BOLD_TEXT": "Cambia la larghezza del testo", 40 | "ITALIC_TEXT": "Cambia lo stile del testo", 41 | "TEXT_SIZE": "Cambia la dimensione del testo e del nodo", 42 | "IMAGE_SIZE": "Cambia la dimensione dell'immage del nodo", 43 | "ADD_NODE": "Aggiunge un nodo", 44 | "REMOVE_NODE": "Rimuove un nodo", 45 | "ZOOM_IN_MAP": "Esegue uno zoom-in", 46 | "ZOOM_OUT_MAP": "Esegue uno zoom-out", 47 | "CENTER_MAP": "Centra la mappa", 48 | "UNDO_MAP": "Annulla la precedente modifica", 49 | "REDO_MAP": "Rieffettua una modifica", 50 | "NODE_COLOR": "Cambia il colore del nodo", 51 | "BRANCH_COLOR": "Cambia il colore del ramo", 52 | "TEXT_COLOR": "Cambia il colore del testo", 53 | "ROOT_NODE_NAME_INPUT": "Cambia il nome predefinito del nodo radice", 54 | "NODE_NAME_INPUT": "Cambia il nome predefinito dei nodi", 55 | "LANGUAGE": "Cambia la lingua", 56 | "SETTINGS": "Apre le impostazioni", 57 | "CENTER_ON_RESIZING": "Centra la mappa quando viene ridimensionata la finestra", 58 | "NODE_DRAGGING": "Permette di trascinare i nodi nella mappa", 59 | "MAP_ZOOM": "Permette di effettuare lo zoom sulla mappa", 60 | "AUTO_BRANCH_COLORS": "Permette di scegliere in modo automatico i colori dei rami" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Applicazione web per creare mappe mentali.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Mappe mentali efficaci", 68 | "Applicazione web moderna", 69 | "Semplice interfaccia", 70 | "Software open source" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mappe mentali", 76 | "SUBTITLE": "Mindmapp segue l'approccio delle mappe mentali di Tony Buzan, che sfruttano l'evocatività delle immagini e dei colori per facilitare la memorizzazione di concetti, stimolando la creatività e le associazioni mentali.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colori e immagini", 80 | "CONTENT": "I colori e le immagini delle mappe sfruttano le caratteristiche del nostro cervello per esprimere i concetti in modo più efficace e duraturo." 81 | }, 82 | { 83 | "TITLE": "Geometria radiale", 84 | "CONTENT": "La struttura gerarchica delle mappe mentali sfrutta una geometria radiale, la quale viene suddivisa su più livelli e facilità l'espressività dei concetti." 85 | }, 86 | { 87 | "TITLE": "Utilizzi", 88 | "CONTENT": "Le mappe mentali sono particolarmente efficaci nelle fasi di apprendimento e nell'organizzazione di idee, per esempio in contesti educativi o aziendali." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Obiettivi", 94 | "SUBTITLE": "Mindmapp si pone come principi cardine la semplicità e l'adattabilità, e sulla base di tali principi cresce secondo le esigenze dell'utente e le tecnologie future, ma assicurando sempre un'applicazione intuitiva.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Design minimale", 98 | "CONTENT": "L'applicazione offre tutti gli strumenti essenziali per la creazione di mappe mentali in modo rapido, ma mantenendo un design semplice ed intuitivo." 99 | }, 100 | { 101 | "TITLE": "Orientata al futuro", 102 | "CONTENT": "Le tecnologie web si evolvono continuamente, e con esse il modo in cui vengono utilizzate. Mindmapp segue ed eventualmente adotta le ultime tecnologie per garantire un servizio sempre migliore." 103 | }, 104 | { 105 | "TITLE": "Personalizzabile", 106 | "CONTENT": "Un sistema compatto ma adattabile ed estendibile rende più semplice raggiungere le singole esigenze dell'utente. L'applicazione crescerà dando particolare importanza a quest'aspetto." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donazioni", 112 | "SUBTITLE": "Mindmapp è gratuita ed il suo codice è pubblico, ogni contributo sarà di grande aiuto per mantenere e migliorare l'applicazione!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "Impostazioni" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "Scorciatoie da tastiera" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "Nessuna mappa salvata!", 125 | "INITIAL_INFORMATION": "Mindmapp è pronto ☺", 126 | "WELCOME_MESSAGE": "Benvenuti nell'app Mindmapp ☺", 127 | "MAP_DELETION_CONFIRM": "Sei sicuro di voler cancellare la mappa?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organizzazione", 131 | "LIBRARY": "Libreria", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Applicazione", 134 | "TRY_IT": "Provala", 135 | "GENERAL": "Generali", 136 | "MAP_OPTIONS": "Opzioni mappa", 137 | "DISMISS": "Chiudi", 138 | "SAVED_MAPS": "Mappe salvate", 139 | "JSON": "File (.json)", 140 | "PNG": "Immagine (.png)", 141 | "JPG": "Immagine (.jpg)", 142 | "PDF": "Documento (.pdf)", 143 | "LANGUAGE": "Lingua", 144 | "YES": "Si", 145 | "NO": "No", 146 | "OK": "Ok", 147 | "SAVE": "Salva", 148 | "OPEN": "Apri", 149 | "CANCEL": "Annulla", 150 | "MAIN": "Principali", 151 | "CENTER_ON_RESIZING": "Centra al ridimensionamento", 152 | "NODE_DRAGGING": "Trascinamento dei nodi", 153 | "MAP_ZOOM": "Zoom della mappa", 154 | "AUTO_BRANCH_COLORS": "Colori automatici per i rami", 155 | "MAP": "Mappa", 156 | "NODES": "Nodi", 157 | "SAVED": "Salvato", 158 | "NOT_SAVED": "Non salvato", 159 | "ROOT_NODE_NAME": "Nome nodo radice", 160 | "NODE_NAME": "Nome nodo" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "Inglês", 4 | "FR": "Francês", 5 | "IT": "Italiano", 6 | "ZH-TW": "Chinês tradicional", 7 | "ZH-CN": "Chinês simplificado", 8 | "PT-BR": "Português do Brasil", 9 | "ES": "Espanhola", 10 | "DE": "Alemão" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Imports the map", 14 | "EXPORT_MAP": "Exports the map", 15 | "ESC": "Fechar a janela de diálogo", 16 | "FULLSCREEN": "Modo tela cheia", 17 | "CLOSE_MENU": "Fechar o menu lateral", 18 | "OPEN_MENU": "Abrir o menu lateral", 19 | "SHORTCUTS": "Mostrar os atalhos", 20 | "MENU": "Abrir o menu", 21 | "NEW_MAP": "Limpar mapa", 22 | "OPEN_MAP": "Carregar mapa existente", 23 | "SAVE_MAP": "Salvar mapa", 24 | "SAVE_MAP_WITH_NAME": "Salvar mapa como", 25 | "COPY_NODE": "Copiar o nó atual", 26 | "CUT_NODE": "Recortar o nó atual", 27 | "PASTE_NODE": "Colar o nó copiado", 28 | "MOVE_NODE_TO_THE_LEFT": "Mover o nó para a esquerda", 29 | "MOVE_NODE_TO_THE_RIGHT": "Mover o nó para a direita", 30 | "MOVE_NODE_DOWN": "Mover o nó para baixo", 31 | "MOVE_NODE_UPWARD": "Mover o nó para cima", 32 | "SELECT_NODE_ON_THE_LEFT": "Selecionar o nó à esquerda", 33 | "SELECT_NODE_ON_THE_RIGHT": "Selecionar o nó à direita", 34 | "SELECT_NODE_BELOW": "Selecionar o nó abaixo", 35 | "SELECT_NODE_ABOVE": "Selecionar o nó acima", 36 | "EDIT_NODE": "Permitir editar o texto do nó", 37 | "LOCK_NODE": "Bloqueia ou desbloqueia o nó", 38 | "NODE_IMAGE": "Adiciona uma imagem ao nó", 39 | "BOLD_TEXT": "Negrito", 40 | "ITALIC_TEXT": "Itálico", 41 | "TEXT_SIZE": "Alterar o tamanho do texto e nó", 42 | "IMAGE_SIZE": "Alterar o tamanho da imagem do nó", 43 | "ADD_NODE": "Adicionar um nó", 44 | "REMOVE_NODE": "Remover um nó", 45 | "ZOOM_IN_MAP": "Ampliar o mapa", 46 | "ZOOM_OUT_MAP": "Diminuir o mapa", 47 | "CENTER_MAP": "Centralizar o mapa", 48 | "UNDO_MAP": "Anular a última alteração", 49 | "REDO_MAP": "Repete uma alteração anteriormente desfeita", 50 | "NODE_COLOR": "Alterar a cor do nó", 51 | "BRANCH_COLOR": "Alterar a cor do ramo", 52 | "TEXT_COLOR": "Alterar a cor do texto", 53 | "ROOT_NODE_NAME_INPUT": "Alterar o nome do nó raiz padrão", 54 | "NODE_NAME_INPUT": "Alterar o nome do nó padrão", 55 | "LANGUAGE": "Altera o idioma", 56 | "SETTINGS": "Abrir as configurações", 57 | "CENTER_ON_RESIZING": "Centralizar o mapa no redimensionamento da janela", 58 | "NODE_DRAGGING": "Permite arrastar os nós no mapa", 59 | "MAP_ZOOM": "Permite ampliar o mapa", 60 | "AUTO_BRANCH_COLORS": "Permite escolher automaticamente as cores dos ramos" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "Configurações" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "Atalhos" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "No saved maps!", 125 | "INITIAL_INFORMATION": "Mindmapp está pronto ☺", 126 | "WELCOME_MESSAGE": "Bem vindo ao Mindmapp app ☺", 127 | "MAP_DELETION_CONFIRM": "Are you sure you want to delete the map?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organization", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Application", 134 | "TRY_IT": "Try it", 135 | "GENERAL": "Geral", 136 | "MAP_OPTIONS": "Opções de mapa", 137 | "DISMISS": "Dispensar", 138 | "SAVED_MAPS": "Saved maps", 139 | "JSON": "File (.json)", 140 | "PNG": "Imagem (.png)", 141 | "JPG": "Imagem (.jpg)", 142 | "PDF": "Documento (.pdf)", 143 | "LANGUAGE": "Linguagem", 144 | "YES": "Sim", 145 | "NO": "Não", 146 | "OK": "Ok", 147 | "SAVE": "Salvar", 148 | "OPEN": "Abrir", 149 | "CANCEL": "Cancelar", 150 | "MAIN": "Principal", 151 | "CENTER_ON_RESIZING": "Centralizar no redimensionamento", 152 | "NODE_DRAGGING": "Arrastar nó", 153 | "MAP_ZOOM": "Zoom de mapa", 154 | "AUTO_BRANCH_COLORS": "Ramo com cores automáticas", 155 | "MAP": "Mapa", 156 | "NODES": "Nós", 157 | "SAVED": "Salvo", 158 | "NOT_SAVED": "Não salvo", 159 | "ROOT_NODE_NAME": "Nome do nó raiz", 160 | "NODE_NAME": "Nome do nó" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "英语", 4 | "FR": "法语", 5 | "IT": "意大利语", 6 | "ZH-TW": "繁体中文", 7 | "ZH-CN": "简体中文", 8 | "PT-BR": "巴西葡萄牙語", 9 | "ES": "西班牙语", 10 | "DE": "德语" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Imports the map", 14 | "EXPORT_MAP": "Exports the map", 15 | "ESC": "关闭", 16 | "FULLSCREEN": "全屏", 17 | "CLOSE_MENU": "关闭菜单", 18 | "OPEN_MENU": "打开菜单", 19 | "SHORTCUTS": "显示快捷方式", 20 | "MENU": "菜单", 21 | "NEW_MAP": "新建", 22 | "OPEN_MAP": "打开", 23 | "SAVE_MAP": "保存", 24 | "SAVE_MAP_WITH_NAME": "另存为", 25 | "COPY_NODE": "复制节点", 26 | "CUT_NODE": "剪切节点", 27 | "PASTE_NODE": "粘贴节点", 28 | "MOVE_NODE_TO_THE_LEFT": "左移节点", 29 | "MOVE_NODE_TO_THE_RIGHT": "右移节点", 30 | "MOVE_NODE_DOWN": "下移节点", 31 | "MOVE_NODE_UPWARD": "上移节点", 32 | "SELECT_NODE_ON_THE_LEFT": "选择左侧节点", 33 | "SELECT_NODE_ON_THE_RIGHT": "选择右侧节点", 34 | "SELECT_NODE_BELOW": "选择下方节点", 35 | "SELECT_NODE_ABOVE": "选择上方节点", 36 | "EDIT_NODE": "编辑节点", 37 | "LOCK_NODE": "锁定节点", 38 | "NODE_IMAGE": "向节点添加图片", 39 | "BOLD_TEXT": "粗体", 40 | "ITALIC_TEXT": "斜体", 41 | "TEXT_SIZE": "文字大小", 42 | "IMAGE_SIZE": "图片大小", 43 | "ADD_NODE": "新增节点", 44 | "REMOVE_NODE": "删除节点", 45 | "ZOOM_IN_MAP": "放大", 46 | "ZOOM_OUT_MAP": "缩小", 47 | "CENTER_MAP": "置中", 48 | "UNDO_MAP": "撤销", 49 | "REDO_MAP": "重做", 50 | "NODE_COLOR": "节点颜色", 51 | "BRANCH_COLOR": "分支颜色", 52 | "TEXT_COLOR": "文字颜色", 53 | "ROOT_NODE_NAME_INPUT": "根节点名称", 54 | "NODE_NAME_INPUT": "节点名称", 55 | "LANGUAGE": "语言", 56 | "SETTINGS": "设置", 57 | "CENTER_ON_RESIZING": "置中缩放", 58 | "NODE_DRAGGING": "拖拽节点", 59 | "MAP_ZOOM": "缩放地图", 60 | "AUTO_BRANCH_COLORS": "自动分配分支颜色" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "设置" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "快捷键" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "No saved maps!", 125 | "INITIAL_INFORMATION": "Mindmapp 已经准备好了 ☺", 126 | "WELCOME_MESSAGE": "欢迎使用 Mindmapp app ☺", 127 | "MAP_DELETION_CONFIRM": "Are you sure you want to delete the map?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organization", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Application", 134 | "TRY_IT": "Try it", 135 | "GENERAL": "一般", 136 | "MAP_OPTIONS": "思维导图选项", 137 | "DISMISS": "解除", 138 | "SAVED_MAPS": "Saved maps", 139 | "JSON": "File (.json)", 140 | "PNG": "图片 (.png)", 141 | "JPG": "图片 (.jpg)", 142 | "PDF": "文档 (.pdf)", 143 | "LANGUAGE": "语言", 144 | "YES": "是", 145 | "NO": "否", 146 | "OK": "确定", 147 | "SAVE": "保存", 148 | "OPEN": "打开", 149 | "CANCEL": "取消", 150 | "MAIN": "主界面", 151 | "CENTER_ON_RESIZING": "缩放之后居中", 152 | "NODE_DRAGGING": "拖拽节点", 153 | "MAP_ZOOM": "缩放", 154 | "AUTO_BRANCH_COLORS": "自动分配分支颜色", 155 | "MAP": "思维导图", 156 | "NODES": "节点", 157 | "SAVED": "已保存", 158 | "NOT_SAVED": "未保存", 159 | "ROOT_NODE_NAME": "根节点名称", 160 | "NODE_NAME": "节点名称" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/i18n/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "LANGUAGES": { 3 | "EN": "英文", 4 | "FR": "法文", 5 | "IT": "義大利文", 6 | "ZH-TW": "繁體中文", 7 | "ZH-CN": "简体中文", 8 | "PT-BR": "巴西葡萄牙語", 9 | "ES": "西班牙語", 10 | "DE": "德語" 11 | }, 12 | "TOOLTIPS": { 13 | "IMPORT_MAP": "Imports the map", 14 | "EXPORT_MAP": "Exports the map", 15 | "ESC": "關閉對話窗口", 16 | "FULLSCREEN": "將應用程式全螢幕", 17 | "CLOSE_MENU": "關閉選單", 18 | "OPEN_MENU": "打開選單", 19 | "SHORTCUTS": "顯示快捷鍵方式", 20 | "MENU": "開啟選單", 21 | "NEW_MAP": "新增地圖", 22 | "OPEN_MAP": "載入現有地圖", 23 | "SAVE_MAP": "儲存地圖", 24 | "SAVE_MAP_WITH_NAME": "另存新檔", 25 | "COPY_NODE": "複製選取的節點", 26 | "CUT_NODE": "剪下選取的節點", 27 | "PASTE_NODE": "貼上已複製的節點", 28 | "MOVE_NODE_TO_THE_LEFT": "將節點向左移動", 29 | "MOVE_NODE_TO_THE_RIGHT": "將節點向右移動", 30 | "MOVE_NODE_DOWN": "將節點向下移動", 31 | "MOVE_NODE_UPWARD": "將節點向上移動", 32 | "SELECT_NODE_ON_THE_LEFT": "選擇左側的節點", 33 | "SELECT_NODE_ON_THE_RIGHT": "選擇右側的節點", 34 | "SELECT_NODE_BELOW": "選擇下方的節點", 35 | "SELECT_NODE_ABOVE": "選擇上方的節點", 36 | "EDIT_NODE": "編輯選取的節點文字", 37 | "LOCK_NODE": "鎖定選取的節點", 38 | "NODE_IMAGE": "新增圖片於當前節點", 39 | "BOLD_TEXT": "將文字變成粗體", 40 | "ITALIC_TEXT": "將文字變為斜體", 41 | "TEXT_SIZE": "變更節點文字大小", 42 | "IMAGE_SIZE": "變更節點圖片大小", 43 | "ADD_NODE": "新增節點", 44 | "REMOVE_NODE": "移除節點", 45 | "ZOOM_IN_MAP": "放大地圖", 46 | "ZOOM_OUT_MAP": "縮小地圖", 47 | "CENTER_MAP": "置中地圖", 48 | "UNDO_MAP": "復原上一次動作", 49 | "REDO_MAP": "重做上一次動作", 50 | "NODE_COLOR": "變更節點的顏色", 51 | "BRANCH_COLOR": "變更分支的顏色", 52 | "TEXT_COLOR": "變更文字的顏色", 53 | "ROOT_NODE_NAME_INPUT": "根節點的預設名稱", 54 | "NODE_NAME_INPUT": "節點的預設名稱", 55 | "LANGUAGE": "變更語系", 56 | "SETTINGS": "開啟設定", 57 | "CENTER_ON_RESIZING": "當視窗縮放後地圖將會被置中", 58 | "NODE_DRAGGING": "允許拖曳地圖中的節點", 59 | "MAP_ZOOM": "允許縮放地圖", 60 | "AUTO_BRANCH_COLORS": "允許自動分配分支的顏色" 61 | }, 62 | "PAGES": { 63 | "ABOUT": { 64 | "INTRODUCTION": { 65 | "APPLICATION_DESCRIPTION": "Web application to draw mind maps.", 66 | "APPLICATION_PROPERTIES": [ 67 | "Effective mind maps", 68 | "Modern web application", 69 | "Simple interface", 70 | "Open source software" 71 | ] 72 | }, 73 | "SECTIONS": [ 74 | { 75 | "TITLE": "Mind maps", 76 | "SUBTITLE": "Mindmapp follows the Tony Buzan approach, which uses the evocative power of images and colors to make the memorization of concepts easier, stimulating creativity and mental associations.", 77 | "CARDS": [ 78 | { 79 | "TITLE": "Colors and images", 80 | "CONTENT": "Colors and images of mind maps exploit brain characteristics to express concepts in a more effective and lasting way." 81 | }, 82 | { 83 | "TITLE": "Radial tree", 84 | "CONTENT": "Hierarchical structure of mind maps exploits the radial geometry, which is divided on several levels and facilitates concept expressiveness." 85 | }, 86 | { 87 | "TITLE": "Uses", 88 | "CONTENT": "Mind maps are particularly effective to learn or organize ideas, for example in educational or business contexts." 89 | } 90 | ] 91 | }, 92 | { 93 | "TITLE": "Goals", 94 | "SUBTITLE": "Mindmapp is based on the simplicity and adaptability principles, and according to these principles it grows following the user's needs and future technologies, but always ensuring an intuitive application.", 95 | "CARDS": [ 96 | { 97 | "TITLE": "Minimal design", 98 | "CONTENT": "The application offers all the essential tools to create mind maps quickly, but maintaining a simple and intuitive design." 99 | }, 100 | { 101 | "TITLE": "Future-oriented", 102 | "CONTENT": "Web technologies are constantly evolving, and with them the way they are used. Mindmapp follows and eventually adopts the latest technologies to ensure a better service." 103 | }, 104 | { 105 | "TITLE": "Customizable", 106 | "CONTENT": "A compact but adaptable and extensible system makes it easier to meet the user's individual needs. The application will grow giving particular importance to this aspect." 107 | } 108 | ] 109 | }, 110 | { 111 | "TITLE": "Donations", 112 | "SUBTITLE": "Mindmapp is free and its code is public, every contribution is very helpful to maintain and improve the application!" 113 | } 114 | ] 115 | }, 116 | "SETTINGS": { 117 | "TITLE": "設定" 118 | }, 119 | "SHORTCUTS": { 120 | "TITLE": "快捷鍵" 121 | } 122 | }, 123 | "MESSAGES": { 124 | "NO_SAVED_MAPS": "No saved maps!", 125 | "INITIAL_INFORMATION": "Mindmapp 已準備好了 ☺", 126 | "WELCOME_MESSAGE": "歡迎使用 Mindmapp app ☺", 127 | "MAP_DELETION_CONFIRM": "Are you sure you want to delete the map?" 128 | }, 129 | "GENERAL": { 130 | "ORGANIZATION": "Organization", 131 | "LIBRARY": "Library", 132 | "REPOSITORY": "Repository", 133 | "APPLICATION": "Application", 134 | "TRY_IT": "Try it", 135 | "GENERAL": "一般", 136 | "MAP_OPTIONS": "地圖選項", 137 | "DISMISS": "解除", 138 | "SAVED_MAPS": "Saved maps", 139 | "JSON": "File (.json)", 140 | "PNG": "圖片 (.png)", 141 | "JPG": "圖片 (.jpg)", 142 | "PDF": "文件 (.pdf)", 143 | "LANGUAGE": "語系", 144 | "YES": "是", 145 | "NO": "否", 146 | "OK": "確定", 147 | "SAVE": "儲存", 148 | "OPEN": "開啟", 149 | "CANCEL": "取消", 150 | "MAIN": "主畫面", 151 | "CENTER_ON_RESIZING": "視窗縮放後置中", 152 | "NODE_DRAGGING": "拖曳節點", 153 | "MAP_ZOOM": "地圖縮放", 154 | "AUTO_BRANCH_COLORS": "自動分配分支顏色", 155 | "MAP": "地圖", 156 | "NODES": "節點", 157 | "SAVED": "已儲存", 158 | "NOT_SAVED": "尚未儲存", 159 | "ROOT_NODE_NAME": "根節點名稱", 160 | "NODE_NAME": "節點名稱" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/icons/icon-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-1024x1024.png -------------------------------------------------------------------------------- /src/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /src/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /src/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /src/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /src/assets/icons/light-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/icons/light-icon.png -------------------------------------------------------------------------------- /src/assets/images/business-plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/images/business-plan.png -------------------------------------------------------------------------------- /src/assets/images/radial-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/images/radial-tree.png -------------------------------------------------------------------------------- /src/assets/images/readme-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/images/readme-header.png -------------------------------------------------------------------------------- /src/assets/images/screens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/images/screens.png -------------------------------------------------------------------------------- /src/assets/images/solar-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedoor/mindmapp/893cb7667f35ee6d1ca02c6909233e9b3e0bc443/src/assets/images/solar-system.png -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | } 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false 3 | } 4 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mindmapp 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import {enableProdMode} from '@angular/core' 2 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic' 3 | import 'hammerjs' 4 | 5 | import {RootModule} from './app/root.module' 6 | import {environment} from './environments/environment' 7 | 8 | if (environment.production) { 9 | enableProdMode() 10 | } 11 | 12 | setTimeout(() => { 13 | platformBrowserDynamic().bootstrapModule(RootModule).catch(console.error) 14 | }, 0) 15 | -------------------------------------------------------------------------------- /src/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mindmapp", 3 | "short_name": "Mindmapp", 4 | "theme_color": "#37474f", 5 | "background_color": "#37474f", 6 | "display": "standalone", 7 | "scope": "/", 8 | "start_url": "/", 9 | "icons": [ 10 | { 11 | "src": "assets/icons/icon-72x72.png", 12 | "sizes": "72x72", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "assets/icons/icon-96x96.png", 17 | "sizes": "96x96", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "assets/icons/icon-128x128.png", 22 | "sizes": "128x128", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "assets/icons/icon-144x144.png", 27 | "sizes": "144x144", 28 | "type": "image/png" 29 | }, 30 | { 31 | "src": "assets/icons/icon-152x152.png", 32 | "sizes": "152x152", 33 | "type": "image/png" 34 | }, 35 | { 36 | "src": "assets/icons/icon-192x192.png", 37 | "sizes": "192x192", 38 | "type": "image/png" 39 | }, 40 | { 41 | "src": "assets/icons/icon-384x384.png", 42 | "sizes": "384x384", 43 | "type": "image/png" 44 | }, 45 | { 46 | "src": "assets/icons/icon-512x512.png", 47 | "sizes": "512x512", 48 | "type": "image/png" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | /*************************************************************************************************** 17 | * BROWSER POLYFILLS 18 | */ 19 | /** Evergreen browsers require these. */ 20 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 21 | import 'core-js/es/reflect' 22 | /** 23 | * Required to support Web Animations `@angular/platform-browser/animations`. 24 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 25 | */ 26 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 27 | /*************************************************************************************************** 28 | * Zone JS is required by Angular itself. 29 | */ 30 | import 'zone.js/dist/zone' // Included with Angular CLI. 31 | 32 | 33 | /*************************************************************************************************** 34 | * APPLICATION IMPORTS 35 | */ 36 | 37 | /** 38 | * Date, currency, decimal and percent pipes. 39 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 40 | */ 41 | // import 'intl'; // Run `npm install --save intl`. 42 | /** 43 | * Need to import at least one locale-data with intl. 44 | */ 45 | // import 'intl/locale-data/jsonp/en'; 46 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | @import "~src/theme.scss"; 2 | 3 | @font-face { 4 | font-family: "Source Sans Pro"; 5 | src: url("assets/font/source-sans-pro/SourceSansPro-Regular.ttf") format("truetype"); 6 | } 7 | 8 | html, body { 9 | margin: 0; 10 | padding: 0; 11 | height: 100%; 12 | } 13 | 14 | body { 15 | font-family: "Source Sans Pro", sans; 16 | font-size: 17px; 17 | } 18 | 19 | .container { 20 | max-width: 1170px; 21 | margin: 0 auto; 22 | padding: 0 15px; 23 | } 24 | 25 | ::selection { 26 | color: whitesmoke; 27 | background: mat-color($app-primary); 28 | } 29 | 30 | *:focus { 31 | outline: none; 32 | } 33 | 34 | // Material override 35 | .mat-form-field-appearance-legacy .mat-form-field-underline { 36 | background-color: lightgray !important; 37 | } 38 | 39 | mat-dialog-content.mat-dialog-content { 40 | max-height: none; 41 | } 42 | 43 | // Color picker override 44 | div.color-picker { 45 | border-radius: 2px; 46 | border: 1px solid #a8a8a8 !important; 47 | 48 | div.box:last-child { 49 | display: none !important; 50 | } 51 | 52 | div.arrow { 53 | top: 170px !important; 54 | } 55 | } 56 | 57 | // Snackbar 58 | snack-bar-container.snackbar { 59 | background-color: whitesmoke; 60 | border: 1px solid lightgray; 61 | border-bottom: 0; 62 | color: #555555; 63 | 64 | simple-snack-bar.mat-simple-snackbar { 65 | font-size: 15px; 66 | 67 | button { 68 | color: #555555; 69 | } 70 | } 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing' 4 | import {getTestBed} from '@angular/core/testing' 5 | import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing' 6 | 7 | declare const require: any 8 | 9 | // First, initialize the Angular testing environment. 10 | getTestBed().initTestEnvironment( 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting() 13 | ) 14 | // Then we find all the tests. 15 | const context = require.context('./', true, /\.spec\.ts$/) 16 | // And load the modules. 17 | context.keys().map(context) 18 | -------------------------------------------------------------------------------- /src/theme.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | // Plus imports for other components in your app. 3 | 4 | // Include the common styles for Angular Material. We include this here so that you only 5 | // have to load a single css file for Angular Material in your app. 6 | // Be sure that you only ever include this mixin once! 7 | @include mat-core(); 8 | 9 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 10 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 11 | // hue. Available color palettes: https://www.google.com/design/spec/style/color.html 12 | $app-primary: mat-palette($mat-blue-gray, 700); 13 | $app-accent: mat-palette($mat-gray, 50); 14 | 15 | // The warn palette is optional (defaults to red). 16 | $app-warn: mat-palette($mat-red); 17 | 18 | // Create the theme object (a Sass map containing all of the palettes). 19 | $app-theme: mat-light-theme($app-primary, $app-accent, $app-warn); 20 | 21 | // Include theme styles for core and each component used in your app. 22 | // Alternatively, you can import and @include the theme mixins for each component 23 | // that you are using. 24 | @include angular-material-theme($app-theme); 25 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "resolveJsonModule": true, 5 | "outDir": "../out-tsc/app", 6 | "types": [] 7 | }, 8 | "include": [ 9 | "**/*.ts" 10 | ], 11 | "exclude": [ 12 | "test.ts", 13 | "**/*.spec.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "./test.ts", 12 | "./polyfills.ts" 13 | ], 14 | "include": [ 15 | "./**/*.spec.ts", 16 | "./**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "mindmapp", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "mindmapp", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare var module: NodeModule 2 | 3 | interface NodeModule { 4 | id: string 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "space-before-function-paren": [ 5 | true, 6 | { 7 | "anonymous": "always", 8 | "named": "always", 9 | "asyncArrow": "always" 10 | } 11 | ], 12 | "semicolon": [ 13 | true, 14 | "never" 15 | ], 16 | "only-arrow-functions": false, 17 | "array-type": false, 18 | "arrow-parens": false, 19 | "deprecation": { 20 | "severity": "warn" 21 | }, 22 | "component-class-suffix": true, 23 | "contextual-lifecycle": true, 24 | "directive-class-suffix": true, 25 | "directive-selector": [ 26 | true, 27 | "attribute", 28 | "app", 29 | "camelCase" 30 | ], 31 | "component-selector": [ 32 | true, 33 | "element", 34 | "app", 35 | "kebab-case" 36 | ], 37 | "import-blacklist": [ 38 | true, 39 | "rxjs/Rx" 40 | ], 41 | "interface-name": false, 42 | "max-classes-per-file": false, 43 | "max-line-length": [ 44 | true, 45 | 140 46 | ], 47 | "member-access": false, 48 | "member-ordering": [ 49 | true, 50 | { 51 | "order": [ 52 | "static-field", 53 | "instance-field", 54 | "static-method", 55 | "instance-method" 56 | ] 57 | } 58 | ], 59 | "no-consecutive-blank-lines": false, 60 | "no-console": [ 61 | true, 62 | "debug", 63 | "info", 64 | "time", 65 | "timeEnd", 66 | "trace" 67 | ], 68 | "no-empty": false, 69 | "no-inferrable-types": [ 70 | true, 71 | "ignore-params" 72 | ], 73 | "no-non-null-assertion": true, 74 | "no-redundant-jsdoc": true, 75 | "no-switch-case-fall-through": true, 76 | "no-use-before-declare": true, 77 | "no-var-requires": false, 78 | "object-literal-key-quotes": [ 79 | true, 80 | "as-needed" 81 | ], 82 | "object-literal-sort-keys": false, 83 | "ordered-imports": false, 84 | "quotemark": [ 85 | true, 86 | "single" 87 | ], 88 | "trailing-comma": false, 89 | "no-conflicting-lifecycle": true, 90 | "no-host-metadata-property": true, 91 | "no-input-rename": true, 92 | "no-inputs-metadata-property": true, 93 | "no-output-native": true, 94 | "no-output-on-prefix": true, 95 | "no-output-rename": true, 96 | "no-outputs-metadata-property": true, 97 | "template-banana-in-box": true, 98 | "template-no-negated-async": true, 99 | "use-lifecycle-interface": true, 100 | "use-pipe-transform-interface": true 101 | }, 102 | "rulesDirectory": [ 103 | "codelyzer" 104 | ] 105 | } 106 | --------------------------------------------------------------------------------