From 04f869fd86566a79aff92d6777de983d27655598 Mon Sep 17 00:00:00 2001 From: FrogTheFrog <warliukz@gmail.com> Date: Thu, 16 Nov 2017 00:23:01 +0200 Subject: [PATCH] update 2.2.9 --- CHANGELOG.md | 13 + package-lock.json | 117 ++- package.json | 25 +- src/lang/english/langData.ts | 47 +- src/lang/english/markdown/default-image.md | 13 + src/lang/english/markdown/fuzzy-matching.md | 8 +- src/lang/english/markdown/intro.md | 8 +- src/lang/english/markdown/parser-variables.md | 11 +- src/lib/diacritic.json | 826 ++++++++++++++++++ src/lib/file-parser.ts | 45 +- src/lib/file-selector.ts | 35 + src/lib/fuzzy-matcher.ts | 34 +- src/lib/helpers.ts | 45 +- src/lib/image-providers/generic-provider.ts | 2 + .../retrogaming-cloud.worker.ts | 2 +- src/lib/index.ts | 3 +- src/lib/markdown-variable.ts | 6 +- src/lib/replace-diacritics.ts | 17 + src/lib/vdf-manager.ts | 13 +- src/models/fuzzy.model.ts | 6 + src/models/language.model.ts | 6 + src/models/parser.model.ts | 2 + src/models/preview.model.ts | 11 +- src/models/user-configuration.model.ts | 4 +- src/renderer/app.module.ts | 37 +- src/renderer/app.ts | 1 + src/renderer/components/app.component.ts | 1 + src/renderer/components/markdown.component.ts | 25 +- src/renderer/components/parsers.component.ts | 33 + src/renderer/components/preview.component.ts | 70 +- .../schemas/user-configuration.schema.ts | 4 +- src/renderer/services/parsers.service.ts | 3 +- src/renderer/services/preview.service.ts | 56 +- src/renderer/templates/preview.component.html | 11 +- webpack/renderer.js | 2 +- 35 files changed, 1276 insertions(+), 266 deletions(-) create mode 100644 src/lang/english/markdown/default-image.md create mode 100644 src/lib/diacritic.json create mode 100644 src/lib/file-selector.ts create mode 100644 src/lib/replace-diacritics.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7759cb2bef..1a79413283 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ # Change Log All notable changes to this project will be documented in this file. +## 2.2.9 - 2017-11-16 + +### Added + +* Fuzzy matcher now has an option to replace diacritic characters to their latin equivalent. Available character list is probably not full, so if you find a missing character be sure to post an issue. +* Parser variable added which can replace diacritic characters to their latin equivalent. +* Added default/fallback image option for when there is no image available. + +### Fixed + +* `substr` error when glob contains space characters at the start of input. +* Could not add local image manually most of the time due tue file input being removed before callback is fired. + ## 2.2.8 - 2017-11-12 ### Added diff --git a/package-lock.json b/package-lock.json index 95e5e66b8d..331ff7ce73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "steam-rom-manager", - "version": "2.2.8", + "version": "2.2.9", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -21,73 +21,73 @@ "optional": true }, "@angular/animations": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.4.6.tgz", - "integrity": "sha1-+mYYmaik44y3xYPHpcl85l1ZKjU=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.0.1.tgz", + "integrity": "sha1-qSsrGGpuWjGp8VhJEd1qp+FsXeE=", "requires": { "tslib": "1.8.0" } }, "@angular/common": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.4.6.tgz", - "integrity": "sha1-S4FCByTggooOg5uVpV6xp+g5GPI=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.0.1.tgz", + "integrity": "sha1-QwBas8i4/68Xaq+zuGupMcPkvfk=", "requires": { "tslib": "1.8.0" } }, "@angular/compiler": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.4.6.tgz", - "integrity": "sha1-LuH68lt1fh0SiXkHS+f65SmzvCA=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.0.1.tgz", + "integrity": "sha1-f9TH+ku770wUaWL6lGuCczCmyO0=", "requires": { "tslib": "1.8.0" } }, "@angular/core": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.4.6.tgz", - "integrity": "sha1-EwMf0Q3P5DiHVBmzjyESCVi8I1Q=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.0.1.tgz", + "integrity": "sha1-pKdK/H4gWNMLgmPrbWbarOn0J7o=", "requires": { "tslib": "1.8.0" } }, "@angular/forms": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.4.6.tgz", - "integrity": "sha1-/mSs5CQ1wbgPSQNLfEHOjK8UpEo=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.0.1.tgz", + "integrity": "sha1-afMDxME9o8qg3mNDdYg4i2rWKyE=", "requires": { "tslib": "1.8.0" } }, "@angular/http": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.4.6.tgz", - "integrity": "sha1-CvaAxnEL3AJtlA4iXP0PalwAXQw=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-5.0.1.tgz", + "integrity": "sha1-NQy99jz6yJOWE9dT/wce1YpgVhs=", "requires": { "tslib": "1.8.0" } }, "@angular/platform-browser": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.4.6.tgz", - "integrity": "sha1-qYOcVH4bZU+h0kqJeAyLpquNzOA=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.0.1.tgz", + "integrity": "sha1-FIld0w7Sow7nuZx2t2R0j0bBqGI=", "requires": { "tslib": "1.8.0" } }, "@angular/platform-browser-dynamic": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.4.6.tgz", - "integrity": "sha1-TT2aanvyzz3kBYphWuBZ7/ZB+jY=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.0.1.tgz", + "integrity": "sha1-Fttn1S1FMVY6sVQpxr3+GLwb7cg=", "requires": { "tslib": "1.8.0" } }, "@angular/router": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.4.6.tgz", - "integrity": "sha1-D2rSmuD/jSyeo3m9MgRHIXt+yGY=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-5.0.1.tgz", + "integrity": "sha1-msCPKTAu9gzf08eBDZbCZd7EY9Y=", "requires": { "tslib": "1.8.0" } @@ -115,16 +115,16 @@ "integrity": "sha1-I2a+tDmc1zSzPkLHrICVduYX1Io=", "dev": true, "requires": { - "@types/node": "8.0.51" + "@types/node": "8.0.52" } }, "@types/fs-extra": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-4.0.4.tgz", - "integrity": "sha512-u2LUBibrqjpF/AJ9trGmpIZMBQ8zjLqDHO7f25gNfNHI+/cCvFjut7u2s7Dn6tbETFBgJEjb/INRDCbTF8nJUA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-4.0.5.tgz", + "integrity": "sha512-tIG0GpHum5IFb8Qze/cSv0w/0gNzHB+MUDftTQaxenx46z50g51/MPkNLssLz9+uZLzCDd35bT9qtWOTXZ21Gw==", "dev": true, "requires": { - "@types/node": "8.0.51" + "@types/node": "8.0.52" } }, "@types/fuzzaldrin-plus": { @@ -140,7 +140,7 @@ "dev": true, "requires": { "@types/minimatch": "3.0.1", - "@types/node": "8.0.51" + "@types/node": "8.0.52" } }, "@types/he": { @@ -180,9 +180,9 @@ "dev": true }, "@types/node": { - "version": "8.0.51", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", - "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "version": "8.0.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.52.tgz", + "integrity": "sha512-wOU/VRodnI/4Chxuu6R6bcyN9aE3rztO0i8R76PZO7+DxTXWy60nseGN4ujspucmxrfj5mzgCYPXiXqrD6KC3Q==", "dev": true }, "@types/rangy": { @@ -599,7 +599,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000760", + "caniuse-db": "1.0.30000764", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.18", @@ -977,7 +977,7 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000760", + "caniuse-db": "1.0.30000764", "electron-to-chromium": "1.3.27" } }, @@ -1182,15 +1182,15 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000760", + "caniuse-db": "1.0.30000764", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" } }, "caniuse-db": { - "version": "1.0.30000760", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000760.tgz", - "integrity": "sha1-PqKUc+t4psywny63Osnh3r/sUo0=", + "version": "1.0.30000764", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000764.tgz", + "integrity": "sha1-1zqxGuYvap4vaYZ9bZwjrj8uXY0=", "dev": true }, "capture-stack-trace": { @@ -2160,15 +2160,15 @@ "integrity": "sha1-rdVOn4+D7QL2UZ7BATX2mLGTNs8=", "dev": true, "requires": { - "@types/node": "7.0.46", + "@types/node": "7.0.47", "electron-download": "3.3.0", "extract-zip": "1.6.6" }, "dependencies": { "@types/node": { - "version": "7.0.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.46.tgz", - "integrity": "sha512-u+JAi1KtmaUoU/EHJkxoiuvzyo91FCE41Z9TZWWcOUU3P8oUdlDLdrGzCGWySPgbRMD17B0B+1aaJLYI9egQ6A==", + "version": "7.0.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.47.tgz", + "integrity": "sha512-otFGHHuTiQL7TNK/V44kSwcXuwgiQr96gM6cEKiw1trqjPtfvp9FgBDJTWd7n5aCpkfdko3QgVg9wwmxGqAE2g==", "dev": true } } @@ -4524,9 +4524,9 @@ "dev": true }, "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", "dev": true }, "nanomatch": { @@ -4576,11 +4576,6 @@ "steed": "1.1.3" } }, - "ng-dynamic": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/ng-dynamic/-/ng-dynamic-3.0.2.tgz", - "integrity": "sha1-smhCDaHYRbvoXjnG9aA07eIfK2E=" - }, "ngx-color-picker": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-5.0.1.tgz", @@ -4710,7 +4705,7 @@ "lodash.mergewith": "4.6.0", "meow": "3.7.0", "mkdirp": "0.5.1", - "nan": "2.7.0", + "nan": "2.8.0", "node-gyp": "3.6.2", "npmlog": "4.1.2", "request": "2.83.0", @@ -6418,7 +6413,7 @@ "dev": true, "requires": { "bindings": "1.3.0", - "nan": "2.7.0", + "nan": "2.8.0", "prebuild-install": "2.3.0" } }, @@ -7590,13 +7585,13 @@ "chownr": "1.0.1", "mkdirp": "0.5.1", "pump": "1.0.2", - "tar-stream": "1.5.4" + "tar-stream": "1.5.5" } }, "tar-stream": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", - "integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", + "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", "dev": true, "requires": { "bl": "1.2.1", diff --git a/package.json b/package.json index fa590ee078..5ebecee09c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "steam-rom-manager", - "version": "2.2.8", + "version": "2.2.9", "license": "GPL-3.0", "description": "An app for managing ROMs in Steam", "author": { @@ -62,15 +62,15 @@ "build:mac": "build --mac --ia32 --x64" }, "dependencies": { - "@angular/animations": "^4.4.6", - "@angular/common": "^4.4.6", - "@angular/compiler": "^4.4.6", - "@angular/core": "^4.4.6", - "@angular/forms": "^4.4.6", - "@angular/http": "^4.4.6", - "@angular/platform-browser": "^4.4.6", - "@angular/platform-browser-dynamic": "^4.4.6", - "@angular/router": "^4.4.6", + "@angular/animations": "^5.0.1", + "@angular/common": "^5.0.1", + "@angular/compiler": "^5.0.1", + "@angular/core": "^5.0.1", + "@angular/forms": "^5.0.1", + "@angular/http": "^5.0.1", + "@angular/platform-browser": "^5.0.1", + "@angular/platform-browser-dynamic": "^5.0.1", + "@angular/router": "^5.0.1", "@node-steam/vdf": "^1.0.0", "@types/rangy": "0.0.31", "ajv": "^5.3.0", @@ -94,7 +94,6 @@ "markdown-it-attrs": "^0.8.0", "mime-types": "^2.1.17", "minimatch": "^3.0.4", - "ng-dynamic": "^3.0.2", "ngx-color-picker": "^5.0.1", "os-name": "^2.0.1", "rangy": "^1.3.0", @@ -109,7 +108,7 @@ "@types/async": "^2.0.45", "@types/bluebird": "^3.5.18", "@types/crc": "^3.4.0", - "@types/fs-extra": "^4.0.4", + "@types/fs-extra": "^4.0.5", "@types/fuzzaldrin-plus": "0.0.1", "@types/glob": "^5.0.33", "@types/he": "^0.5.29", @@ -118,7 +117,7 @@ "@types/long": "^3.0.32", "@types/markdown-it": "0.0.4", "@types/minimatch": "^3.0.1", - "@types/node": "^8.0.51", + "@types/node": "^8.0.52", "angular2-template-loader": "^0.6.2", "awesome-typescript-loader": "^3.3.0", "cross-env": "^5.1.1", diff --git a/src/lang/english/langData.ts b/src/lang/english/langData.ts index 89eb0fddd9..3b7769543e 100644 --- a/src/lang/english/langData.ts +++ b/src/lang/english/langData.ts @@ -236,6 +236,12 @@ export const EnglishLang: languageContainer = { imagePool: [ require('./markdown/image-pool.md'), ], + defaultImage: [ + require('./markdown/default-image.md'), + require('./markdown/special-glob-input.md'), + require('./markdown/parser-variables.md'), + require('./markdown/spec-glob-chars.md') + ], localImages: [ require('./markdown/local-images.md'), require('./markdown/special-glob-input.md'), @@ -267,25 +273,28 @@ export const EnglishLang: languageContainer = { failedToCopy: 'Failed to copy to clipbard!' }, success: { - foundAccounts__i: 'Found ${count} available user account(s):', - foundAccountInfo__i: ' ${name} (steamID64: ${steamID64}, accountID: ${accountID})', - steamCategory__i: '[${index}/${total}]: Steam categories - ${steamCategory}', - steamCategoryInfo__i: '[${index}/${total}]: ${steamCategory}', - extractedTitle__i: '[${index}/${total}]: Title - ${title}', - fuzzyTitle__i: '[${index}/${total}]: Fuzzy title - ${title}', - finalTitle__i: '[${index}/${total}]: Final title - ${title}', - filePath__i: '[${index}/${total}]: File path - ${filePath}', - completeShortcut__i: '[${index}/${total}]: Complete shortcut - ${shortcut}', - firstImageQuery__i: '[${index}/${total}]: Image queries - ${query}', - imageQueries__i: '[${index}/${total}]: ${query}', - resolvedImageGlob__i: '[${index}/${total}]: Resolved image glob:', - resolvedImageGlobInfo__i: '[${index}/${total}]: ${glob}', - localImagesResolved__i: '[${index}/${total}]: Resolved images:', - localImageInfo__i: '[${index}/${total}]: ${image}', - resolvedIconGlob__i: '[${index}/${total}]: Resolved icon glob:', - resolvedIconGlobInfo__i: '[${index}/${total}]: ${glob}', + foundAccounts__i: 'Found ${count} available user account(s):', + foundAccountInfo__i: ' ${name} (steamID64: ${steamID64}, accountID: ${accountID})', + steamCategory__i: '[${index}/${total}]: Steam categories - ${steamCategory}', + steamCategoryInfo__i: '[${index}/${total}]: ${steamCategory}', + extractedTitle__i: '[${index}/${total}]: Title - ${title}', + fuzzyTitle__i: '[${index}/${total}]: Fuzzy title - ${title}', + finalTitle__i: '[${index}/${total}]: Final title - ${title}', + filePath__i: '[${index}/${total}]: File path - ${filePath}', + completeShortcut__i: '[${index}/${total}]: Complete shortcut - ${shortcut}', + firstImageQuery__i: '[${index}/${total}]: Image queries - ${query}', + imageQueries__i: '[${index}/${total}]: ${query}', + resolvedDefaultImageGlob__i: '[${index}/${total}]: Default image glob:', + resolvedDefaultImageGlobInfo__i: '[${index}/${total}]: ${glob}', + defaultImageResolved__i: '[${index}/${total}]: Default image:\r\n[${index}/${total}]: ${image}', + resolvedImageGlob__i: '[${index}/${total}]: Resolved image glob:', + resolvedImageGlobInfo__i: '[${index}/${total}]: ${glob}', + localImagesResolved__i: '[${index}/${total}]: Resolved images:', + localImageInfo__i: '[${index}/${total}]: ${image}', + resolvedIconGlob__i: '[${index}/${total}]: Resolved icon glob:', + resolvedIconGlobInfo__i: '[${index}/${total}]: ${glob}', localIconsResolved__i: '[${index}/${total}]: Resolved icons:', - localIconInfo__i: '[${index}/${total}]: ${icon}' + localIconInfo__i: '[${index}/${total}]: ${icon}' }, label: { parserType: 'Parser type', @@ -304,6 +313,7 @@ export const EnglishLang: languageContainer = { onlineImageQueries: 'Online image query', imagePool: 'Image pool', imageProviders: 'Image providers', + defaultImage: 'Default image', localImages: 'Local images', localIcons: 'Local icons' }, @@ -320,6 +330,7 @@ export const EnglishLang: languageContainer = { fuzzy_use: 'Use fuzzy matching', fuzzy_removeCharacters: 'Aggressive matching', fuzzy_removeBrackets: 'Remove (...) and [...] brackets', + fuzzy_replaceDiacritic: 'Replace diacritic characters', appendArgsToExecutable: 'Append arguments to executable', disabled: 'Disable current parser', advanced: 'Show advanced options', diff --git a/src/lang/english/markdown/default-image.md b/src/lang/english/markdown/default-image.md new file mode 100644 index 0000000000..52964c4d63 --- /dev/null +++ b/src/lang/english/markdown/default-image.md @@ -0,0 +1,13 @@ +# Default image (optional) `[supports variables]`{.noWrap} + +Allows to use image, stored locally, as a default/fallback image. A [special glob input](#special-glob-input) string is used to retrieve images. Only the first retrieved image is used. + +This image will be shown **only** if there are no other images available. If Steam image is available, you will be able to choose from Steam and this image. + +## Allowed image extensions + +Only `JPEG`{.noWrap}, `JPG`{.noWrap}, `PNG`{.noWrap} and `TGA`{.noWrap} file extensions are supported. Even if parser finds files with other extensions, they are not included into the final list. + +## Can you move the directory of default image after saving app list? + +Yes, once the list is saved, default image is copied to a Steam directory where they are renamed to match Steam's APP ID. diff --git a/src/lang/english/markdown/fuzzy-matching.md b/src/lang/english/markdown/fuzzy-matching.md index 276020aea5..70634f0d21 100644 --- a/src/lang/english/markdown/fuzzy-matching.md +++ b/src/lang/english/markdown/fuzzy-matching.md @@ -4,10 +4,14 @@ When enabled, fuzzy (natural) matching will be done against the tittle list prov It is possible, that `false` matching might occur for titles that are not in the list. If you encounter missing titles, feel free to post an issue on [github](https://github.com/FrogTheFrog/steam-rom-manager/issues). -## Aggressive matching +## Replace diacritic characters -When enabled, fuzzy matcher will remove all characters except for `a-zA-Z0-9 ()[]` and will replace `_` with space. This should improve fuzzy the matching ability of fuzzy matcher. +Replaces diacritic characters to their latin equivalent: `Ą` -> `A`, `Ę` -> `E`, `Ė` -> `E`, etc. Might improve the matching ability of fuzzy matcher. ## Aggressive matching +When enabled, fuzzy matcher will remove all characters except for `a-zA-Z0-9 ()[]` and will replace `_` with space. This should improve the matching ability of fuzzy matcher. + +## Remove (...) and [...] brackets + When enabled, fuzzy matcher will remove all `(...)`{.noWrap} and `[...]`{.noWrap} together with their content. Useful for titles with `[USA]`{.noWrap}, `[JPN]`{.noWrap} and etc., as they prevent from matching titles correctly. \ No newline at end of file diff --git a/src/lang/english/markdown/intro.md b/src/lang/english/markdown/intro.md index bde54043b9..b641b35cbb 100644 --- a/src/lang/english/markdown/intro.md +++ b/src/lang/english/markdown/intro.md @@ -1,6 +1,6 @@ # Welcome to parser configuration! -Configuring a parser might look overwhelming at first, but it is easier than you think. If you are lost, click on #{svg[info-icon]} near option label to see related information which might be useful for you. +Configuring a parser might look overwhelming at first, but it is easier than you think. If you are lost, click on info icon near option label to see related information which might be useful for you. Also, don't forget to check FAQ. If you still got questions about setting up configuration, visit our official SRM group at [Steam](http://steamcommunity.com/groups/steamrommanager) or our official [Discord](https://discord.gg/nxxzBPJ) group. @@ -8,8 +8,8 @@ Also, don't forget to check FAQ. If you still got questions about setting up con After saving parser configuration, **1** of **3** colors will be shown next to its title: -#{DOM[<span style="margin-bottom: 0.05em;display: inline-block;border-radius: 50%;width: 0.5em;height: 0.5em;background-color: var(--color-nav-link-enabled)"></span>]} -- Enabled configuration. This configuration will be used when generating a list in **preview** page. +<span style="margin-bottom: 0.05em;display: inline-block;border-radius: 50%;width: 0.5em;height: 0.5em;background-color: var(--color-nav-link-enabled)"></span> -- Enabled configuration. This configuration will be used when generating a list in **preview** page. -#{DOM[<span style="margin-bottom: 0.05em;display: inline-block;border-radius: 50%;width: 0.5em;height: 0.5em;background-color: var(--color-nav-link-unsaved)"></span>]} -- Unsaved changes. This configuration will not be used when generating a list in **preview** page, however earlier **saved** version will be used instead. +<span style="margin-bottom: 0.05em;display: inline-block;border-radius: 50%;width: 0.5em;height: 0.5em;background-color: var(--color-nav-link-unsaved)"></span> -- Unsaved changes. This configuration will not be used when generating a list in **preview** page, however earlier **saved** version will be used instead. -#{DOM[<span style="margin-bottom: 0.05em;display: inline-block;border-radius: 50%;width: 0.5em;height: 0.5em;background-color: var(--color-nav-link-disabled)"></span>]} -- Disabled configuration. This configuration will not be used when generating a list in **preview** page. \ No newline at end of file +<span style="margin-bottom: 0.05em;display: inline-block;border-radius: 50%;width: 0.5em;height: 0.5em;background-color: var(--color-nav-link-disabled)"></span> -- Disabled configuration. This configuration will not be used when generating a list in **preview** page. \ No newline at end of file diff --git a/src/lang/english/markdown/parser-variables.md b/src/lang/english/markdown/parser-variables.md index 85e66acebc..c635a633cb 100644 --- a/src/lang/english/markdown/parser-variables.md +++ b/src/lang/english/markdown/parser-variables.md @@ -59,24 +59,27 @@ In case fuzzy matching **fails** or is **disabled**, `${fuzzyTitle}`{.noWrap} is |`${uc|input}`|Uppercase variable. Transforms input to uppercase| |`${lc|input}`|Lowercase variable. Transforms input to lowercase| |`${cv:group|input}`|Change input with matched custom variable (group is optional)| +|`${rdc|input}`|Replace diacritic input characters with their latin equivalent| ### Function variable example -Let's say that `${title}` variable equals to `Zelda (USA) (Disc 1).iso`. Then these variables: +Let's say that `${title}` variable equals to `Pokémon (USA) (Disc 1).iso`. Then these variables: ``` ${/.*/|${title}} //Matches everything ${/(.*)/|${title}} //Captures everything ${/(\(.*?\))/|${title}|} //Captures all brackets and substitutes with nothing ${/(\(Disc\s?[0-9]\))/|${title}} //Captures "Disc..." part ${uc|${/(\(Disc\s?[0-9]\))/|${title}}} //Captures "Disc..." part and transforms it to uppercase +${rdc|${title}} //Replace diacritic characters (in this case: é -> e) ``` will be replaced with these: ``` -Zelda (USA) (Disc 1).iso -Zelda (USA) (Disc 1).iso -Zelda.iso +Pokémon (USA) (Disc 1).iso +Pokémon (USA) (Disc 1).iso +Pokémon.iso (Disc 1) (DISC 1) +Pokemon (USA) (Disc 1).iso ``` ## Other variables diff --git a/src/lib/diacritic.json b/src/lib/diacritic.json new file mode 100644 index 0000000000..a90581885f --- /dev/null +++ b/src/lib/diacritic.json @@ -0,0 +1,826 @@ +{ + "Á": "A", + "Ă": "A", + "Ắ": "A", + "Ặ": "A", + "Ằ": "A", + "Ẳ": "A", + "Ẵ": "A", + "Ǎ": "A", + "Â": "A", + "Ấ": "A", + "Ậ": "A", + "Ầ": "A", + "Ẩ": "A", + "Ẫ": "A", + "Ä": "A", + "Ǟ": "A", + "Ȧ": "A", + "Ǡ": "A", + "Ạ": "A", + "Ȁ": "A", + "À": "A", + "Ả": "A", + "Ȃ": "A", + "Ā": "A", + "Ą": "A", + "Å": "A", + "Ǻ": "A", + "Ḁ": "A", + "Ⱥ": "A", + "Ã": "A", + "Ꜳ": "AA", + "Æ": "AE", + "Ǽ": "AE", + "Ǣ": "AE", + "Ꜵ": "AO", + "Ꜷ": "AU", + "Ꜹ": "AV", + "Ꜻ": "AV", + "Ꜽ": "AY", + "Ḃ": "B", + "Ḅ": "B", + "Ɓ": "B", + "Ḇ": "B", + "Ƀ": "B", + "Ƃ": "B", + "Ć": "C", + "Č": "C", + "Ç": "C", + "Ḉ": "C", + "Ĉ": "C", + "Ċ": "C", + "Ƈ": "C", + "Ȼ": "C", + "Ď": "D", + "Ḑ": "D", + "Ḓ": "D", + "Ḋ": "D", + "Ḍ": "D", + "Ɗ": "D", + "Ḏ": "D", + "Dz": "D", + "Dž": "D", + "Đ": "D", + "Ƌ": "D", + "DZ": "DZ", + "DŽ": "DZ", + "É": "E", + "Ĕ": "E", + "Ě": "E", + "Ȩ": "E", + "Ḝ": "E", + "Ê": "E", + "Ế": "E", + "Ệ": "E", + "Ề": "E", + "Ể": "E", + "Ễ": "E", + "Ḙ": "E", + "Ë": "E", + "Ė": "E", + "Ẹ": "E", + "Ȅ": "E", + "È": "E", + "Ẻ": "E", + "Ȇ": "E", + "Ē": "E", + "Ḗ": "E", + "Ḕ": "E", + "Ę": "E", + "Ɇ": "E", + "Ẽ": "E", + "Ḛ": "E", + "Ꝫ": "ET", + "Ḟ": "F", + "Ƒ": "F", + "Ǵ": "G", + "Ğ": "G", + "Ǧ": "G", + "Ģ": "G", + "Ĝ": "G", + "Ġ": "G", + "Ɠ": "G", + "Ḡ": "G", + "Ǥ": "G", + "Ḫ": "H", + "Ȟ": "H", + "Ḩ": "H", + "Ĥ": "H", + "Ⱨ": "H", + "Ḧ": "H", + "Ḣ": "H", + "Ḥ": "H", + "Ħ": "H", + "Í": "I", + "Ĭ": "I", + "Ǐ": "I", + "Î": "I", + "Ï": "I", + "Ḯ": "I", + "İ": "I", + "Ị": "I", + "Ȉ": "I", + "Ì": "I", + "Ỉ": "I", + "Ȋ": "I", + "Ī": "I", + "Į": "I", + "Ɨ": "I", + "Ĩ": "I", + "Ḭ": "I", + "Ꝺ": "D", + "Ꝼ": "F", + "Ᵹ": "G", + "Ꞃ": "R", + "Ꞅ": "S", + "Ꞇ": "T", + "Ꝭ": "IS", + "Ĵ": "J", + "Ɉ": "J", + "Ḱ": "K", + "Ǩ": "K", + "Ķ": "K", + "Ⱪ": "K", + "Ꝃ": "K", + "Ḳ": "K", + "Ƙ": "K", + "Ḵ": "K", + "Ꝁ": "K", + "Ꝅ": "K", + "Ĺ": "L", + "Ƚ": "L", + "Ľ": "L", + "Ļ": "L", + "Ḽ": "L", + "Ḷ": "L", + "Ḹ": "L", + "Ⱡ": "L", + "Ꝉ": "L", + "Ḻ": "L", + "Ŀ": "L", + "Ɫ": "L", + "Lj": "L", + "Ł": "L", + "LJ": "LJ", + "Ḿ": "M", + "Ṁ": "M", + "Ṃ": "M", + "Ɱ": "M", + "Ń": "N", + "Ň": "N", + "Ņ": "N", + "Ṋ": "N", + "Ṅ": "N", + "Ṇ": "N", + "Ǹ": "N", + "Ɲ": "N", + "Ṉ": "N", + "Ƞ": "N", + "Nj": "N", + "Ñ": "N", + "NJ": "NJ", + "Ó": "O", + "Ŏ": "O", + "Ǒ": "O", + "Ô": "O", + "Ố": "O", + "Ộ": "O", + "Ồ": "O", + "Ổ": "O", + "Ỗ": "O", + "Ö": "O", + "Ȫ": "O", + "Ȯ": "O", + "Ȱ": "O", + "Ọ": "O", + "Ő": "O", + "Ȍ": "O", + "Ò": "O", + "Ỏ": "O", + "Ơ": "O", + "Ớ": "O", + "Ợ": "O", + "Ờ": "O", + "Ở": "O", + "Ỡ": "O", + "Ȏ": "O", + "Ꝋ": "O", + "Ꝍ": "O", + "Ō": "O", + "Ṓ": "O", + "Ṑ": "O", + "Ɵ": "O", + "Ǫ": "O", + "Ǭ": "O", + "Ø": "O", + "Ǿ": "O", + "Õ": "O", + "Ṍ": "O", + "Ṏ": "O", + "Ȭ": "O", + "Ƣ": "OI", + "Ꝏ": "OO", + "Ɛ": "E", + "Ɔ": "O", + "Ȣ": "OU", + "Ṕ": "P", + "Ṗ": "P", + "Ꝓ": "P", + "Ƥ": "P", + "Ꝕ": "P", + "Ᵽ": "P", + "Ꝑ": "P", + "Ꝙ": "Q", + "Ꝗ": "Q", + "Ŕ": "R", + "Ř": "R", + "Ŗ": "R", + "Ṙ": "R", + "Ṛ": "R", + "Ṝ": "R", + "Ȑ": "R", + "Ȓ": "R", + "Ṟ": "R", + "Ɍ": "R", + "Ɽ": "R", + "Ꜿ": "C", + "Ǝ": "E", + "Ś": "S", + "Ṥ": "S", + "Š": "S", + "Ṧ": "S", + "Ş": "S", + "Ŝ": "S", + "Ș": "S", + "Ṡ": "S", + "Ṣ": "S", + "Ṩ": "S", + "Ť": "T", + "Ţ": "T", + "Ṱ": "T", + "Ț": "T", + "Ⱦ": "T", + "Ṫ": "T", + "Ṭ": "T", + "Ƭ": "T", + "Ṯ": "T", + "Ʈ": "T", + "Ŧ": "T", + "Ɐ": "A", + "Ꞁ": "L", + "Ɯ": "M", + "Ʌ": "V", + "Ꜩ": "TZ", + "Ú": "U", + "Ŭ": "U", + "Ǔ": "U", + "Û": "U", + "Ṷ": "U", + "Ü": "U", + "Ǘ": "U", + "Ǚ": "U", + "Ǜ": "U", + "Ǖ": "U", + "Ṳ": "U", + "Ụ": "U", + "Ű": "U", + "Ȕ": "U", + "Ù": "U", + "Ủ": "U", + "Ư": "U", + "Ứ": "U", + "Ự": "U", + "Ừ": "U", + "Ử": "U", + "Ữ": "U", + "Ȗ": "U", + "Ū": "U", + "Ṻ": "U", + "Ų": "U", + "Ů": "U", + "Ũ": "U", + "Ṹ": "U", + "Ṵ": "U", + "Ꝟ": "V", + "Ṿ": "V", + "Ʋ": "V", + "Ṽ": "V", + "Ꝡ": "VY", + "Ẃ": "W", + "Ŵ": "W", + "Ẅ": "W", + "Ẇ": "W", + "Ẉ": "W", + "Ẁ": "W", + "Ⱳ": "W", + "Ẍ": "X", + "Ẋ": "X", + "Ý": "Y", + "Ŷ": "Y", + "Ÿ": "Y", + "Ẏ": "Y", + "Ỵ": "Y", + "Ỳ": "Y", + "Ƴ": "Y", + "Ỷ": "Y", + "Ỿ": "Y", + "Ȳ": "Y", + "Ɏ": "Y", + "Ỹ": "Y", + "Ź": "Z", + "Ž": "Z", + "Ẑ": "Z", + "Ⱬ": "Z", + "Ż": "Z", + "Ẓ": "Z", + "Ȥ": "Z", + "Ẕ": "Z", + "Ƶ": "Z", + "IJ": "IJ", + "Œ": "OE", + "ᴀ": "A", + "ᴁ": "AE", + "ʙ": "B", + "ᴃ": "B", + "ᴄ": "C", + "ᴅ": "D", + "ᴇ": "E", + "ꜰ": "F", + "ɢ": "G", + "ʛ": "G", + "ʜ": "H", + "ɪ": "I", + "ʁ": "R", + "ᴊ": "J", + "ᴋ": "K", + "ʟ": "L", + "ᴌ": "L", + "ᴍ": "M", + "ɴ": "N", + "ᴏ": "O", + "ɶ": "OE", + "ᴐ": "O", + "ᴕ": "OU", + "ᴘ": "P", + "ʀ": "R", + "ᴎ": "N", + "ᴙ": "R", + "ꜱ": "S", + "ᴛ": "T", + "ⱻ": "E", + "ᴚ": "R", + "ᴜ": "U", + "ᴠ": "V", + "ᴡ": "W", + "ʏ": "Y", + "ᴢ": "Z", + "á": "a", + "ă": "a", + "ắ": "a", + "ặ": "a", + "ằ": "a", + "ẳ": "a", + "ẵ": "a", + "ǎ": "a", + "â": "a", + "ấ": "a", + "ậ": "a", + "ầ": "a", + "ẩ": "a", + "ẫ": "a", + "ä": "a", + "ǟ": "a", + "ȧ": "a", + "ǡ": "a", + "ạ": "a", + "ȁ": "a", + "à": "a", + "ả": "a", + "ȃ": "a", + "ā": "a", + "ą": "a", + "ᶏ": "a", + "ẚ": "a", + "å": "a", + "ǻ": "a", + "ḁ": "a", + "ⱥ": "a", + "ã": "a", + "ꜳ": "aa", + "æ": "ae", + "ǽ": "ae", + "ǣ": "ae", + "ꜵ": "ao", + "ꜷ": "au", + "ꜹ": "av", + "ꜻ": "av", + "ꜽ": "ay", + "ḃ": "b", + "ḅ": "b", + "ɓ": "b", + "ḇ": "b", + "ᵬ": "b", + "ᶀ": "b", + "ƀ": "b", + "ƃ": "b", + "ɵ": "o", + "ć": "c", + "č": "c", + "ç": "c", + "ḉ": "c", + "ĉ": "c", + "ɕ": "c", + "ċ": "c", + "ƈ": "c", + "ȼ": "c", + "ď": "d", + "ḑ": "d", + "ḓ": "d", + "ȡ": "d", + "ḋ": "d", + "ḍ": "d", + "ɗ": "d", + "ᶑ": "d", + "ḏ": "d", + "ᵭ": "d", + "ᶁ": "d", + "đ": "d", + "ɖ": "d", + "ƌ": "d", + "ı": "i", + "ȷ": "j", + "ɟ": "j", + "ʄ": "j", + "dz": "dz", + "dž": "dz", + "é": "e", + "ĕ": "e", + "ě": "e", + "ȩ": "e", + "ḝ": "e", + "ê": "e", + "ế": "e", + "ệ": "e", + "ề": "e", + "ể": "e", + "ễ": "e", + "ḙ": "e", + "ë": "e", + "ė": "e", + "ẹ": "e", + "ȅ": "e", + "è": "e", + "ẻ": "e", + "ȇ": "e", + "ē": "e", + "ḗ": "e", + "ḕ": "e", + "ⱸ": "e", + "ę": "e", + "ᶒ": "e", + "ɇ": "e", + "ẽ": "e", + "ḛ": "e", + "ꝫ": "et", + "ḟ": "f", + "ƒ": "f", + "ᵮ": "f", + "ᶂ": "f", + "ǵ": "g", + "ğ": "g", + "ǧ": "g", + "ģ": "g", + "ĝ": "g", + "ġ": "g", + "ɠ": "g", + "ḡ": "g", + "ᶃ": "g", + "ǥ": "g", + "ḫ": "h", + "ȟ": "h", + "ḩ": "h", + "ĥ": "h", + "ⱨ": "h", + "ḧ": "h", + "ḣ": "h", + "ḥ": "h", + "ɦ": "h", + "ẖ": "h", + "ħ": "h", + "ƕ": "hv", + "í": "i", + "ĭ": "i", + "ǐ": "i", + "î": "i", + "ï": "i", + "ḯ": "i", + "ị": "i", + "ȉ": "i", + "ì": "i", + "ỉ": "i", + "ȋ": "i", + "ī": "i", + "į": "i", + "ᶖ": "i", + "ɨ": "i", + "ĩ": "i", + "ḭ": "i", + "ꝺ": "d", + "ꝼ": "f", + "ᵹ": "g", + "ꞃ": "r", + "ꞅ": "s", + "ꞇ": "t", + "ꝭ": "is", + "ǰ": "j", + "ĵ": "j", + "ʝ": "j", + "ɉ": "j", + "ḱ": "k", + "ǩ": "k", + "ķ": "k", + "ⱪ": "k", + "ꝃ": "k", + "ḳ": "k", + "ƙ": "k", + "ḵ": "k", + "ᶄ": "k", + "ꝁ": "k", + "ꝅ": "k", + "ĺ": "l", + "ƚ": "l", + "ɬ": "l", + "ľ": "l", + "ļ": "l", + "ḽ": "l", + "ȴ": "l", + "ḷ": "l", + "ḹ": "l", + "ⱡ": "l", + "ꝉ": "l", + "ḻ": "l", + "ŀ": "l", + "ɫ": "l", + "ᶅ": "l", + "ɭ": "l", + "ł": "l", + "lj": "lj", + "ſ": "s", + "ẜ": "s", + "ẛ": "s", + "ẝ": "s", + "ḿ": "m", + "ṁ": "m", + "ṃ": "m", + "ɱ": "m", + "ᵯ": "m", + "ᶆ": "m", + "ń": "n", + "ň": "n", + "ņ": "n", + "ṋ": "n", + "ȵ": "n", + "ṅ": "n", + "ṇ": "n", + "ǹ": "n", + "ɲ": "n", + "ṉ": "n", + "ƞ": "n", + "ᵰ": "n", + "ᶇ": "n", + "ɳ": "n", + "ñ": "n", + "nj": "nj", + "ó": "o", + "ŏ": "o", + "ǒ": "o", + "ô": "o", + "ố": "o", + "ộ": "o", + "ồ": "o", + "ổ": "o", + "ỗ": "o", + "ö": "o", + "ȫ": "o", + "ȯ": "o", + "ȱ": "o", + "ọ": "o", + "ő": "o", + "ȍ": "o", + "ò": "o", + "ỏ": "o", + "ơ": "o", + "ớ": "o", + "ợ": "o", + "ờ": "o", + "ở": "o", + "ỡ": "o", + "ȏ": "o", + "ꝋ": "o", + "ꝍ": "o", + "ⱺ": "o", + "ō": "o", + "ṓ": "o", + "ṑ": "o", + "ǫ": "o", + "ǭ": "o", + "ø": "o", + "ǿ": "o", + "õ": "o", + "ṍ": "o", + "ṏ": "o", + "ȭ": "o", + "ƣ": "oi", + "ꝏ": "oo", + "ɛ": "e", + "ᶓ": "e", + "ɔ": "o", + "ᶗ": "o", + "ȣ": "ou", + "ṕ": "p", + "ṗ": "p", + "ꝓ": "p", + "ƥ": "p", + "ᵱ": "p", + "ᶈ": "p", + "ꝕ": "p", + "ᵽ": "p", + "ꝑ": "p", + "ꝙ": "q", + "ʠ": "q", + "ɋ": "q", + "ꝗ": "q", + "ŕ": "r", + "ř": "r", + "ŗ": "r", + "ṙ": "r", + "ṛ": "r", + "ṝ": "r", + "ȑ": "r", + "ɾ": "r", + "ᵳ": "r", + "ȓ": "r", + "ṟ": "r", + "ɼ": "r", + "ᵲ": "r", + "ᶉ": "r", + "ɍ": "r", + "ɽ": "r", + "ↄ": "c", + "ꜿ": "c", + "ɘ": "e", + "ɿ": "r", + "ś": "s", + "ṥ": "s", + "š": "s", + "ṧ": "s", + "ş": "s", + "ŝ": "s", + "ș": "s", + "ṡ": "s", + "ṣ": "s", + "ṩ": "s", + "ʂ": "s", + "ᵴ": "s", + "ᶊ": "s", + "ȿ": "s", + "ɡ": "g", + "ᴑ": "o", + "ᴓ": "o", + "ᴝ": "u", + "ť": "t", + "ţ": "t", + "ṱ": "t", + "ț": "t", + "ȶ": "t", + "ẗ": "t", + "ⱦ": "t", + "ṫ": "t", + "ṭ": "t", + "ƭ": "t", + "ṯ": "t", + "ᵵ": "t", + "ƫ": "t", + "ʈ": "t", + "ŧ": "t", + "ᵺ": "th", + "ɐ": "a", + "ᴂ": "ae", + "ǝ": "e", + "ᵷ": "g", + "ɥ": "h", + "ʮ": "h", + "ʯ": "h", + "ᴉ": "i", + "ʞ": "k", + "ꞁ": "l", + "ɯ": "m", + "ɰ": "m", + "ᴔ": "oe", + "ɹ": "r", + "ɻ": "r", + "ɺ": "r", + "ⱹ": "r", + "ʇ": "t", + "ʌ": "v", + "ʍ": "w", + "ʎ": "y", + "ꜩ": "tz", + "ú": "u", + "ŭ": "u", + "ǔ": "u", + "û": "u", + "ṷ": "u", + "ü": "u", + "ǘ": "u", + "ǚ": "u", + "ǜ": "u", + "ǖ": "u", + "ṳ": "u", + "ụ": "u", + "ű": "u", + "ȕ": "u", + "ù": "u", + "ủ": "u", + "ư": "u", + "ứ": "u", + "ự": "u", + "ừ": "u", + "ử": "u", + "ữ": "u", + "ȗ": "u", + "ū": "u", + "ṻ": "u", + "ų": "u", + "ᶙ": "u", + "ů": "u", + "ũ": "u", + "ṹ": "u", + "ṵ": "u", + "ᵫ": "ue", + "ꝸ": "um", + "ⱴ": "v", + "ꝟ": "v", + "ṿ": "v", + "ʋ": "v", + "ᶌ": "v", + "ⱱ": "v", + "ṽ": "v", + "ꝡ": "vy", + "ẃ": "w", + "ŵ": "w", + "ẅ": "w", + "ẇ": "w", + "ẉ": "w", + "ẁ": "w", + "ⱳ": "w", + "ẘ": "w", + "ẍ": "x", + "ẋ": "x", + "ᶍ": "x", + "ý": "y", + "ŷ": "y", + "ÿ": "y", + "ẏ": "y", + "ỵ": "y", + "ỳ": "y", + "ƴ": "y", + "ỷ": "y", + "ỿ": "y", + "ȳ": "y", + "ẙ": "y", + "ɏ": "y", + "ỹ": "y", + "ź": "z", + "ž": "z", + "ẑ": "z", + "ʑ": "z", + "ⱬ": "z", + "ż": "z", + "ẓ": "z", + "ȥ": "z", + "ẕ": "z", + "ᵶ": "z", + "ᶎ": "z", + "ʐ": "z", + "ƶ": "z", + "ɀ": "z", + "ff": "ff", + "ffi": "ffi", + "ffl": "ffl", + "fi": "fi", + "fl": "fl", + "ij": "ij", + "œ": "oe", + "st": "st", + "ₐ": "a", + "ₑ": "e", + "ᵢ": "i", + "ⱼ": "j", + "ₒ": "o", + "ᵣ": "r", + "ᵤ": "u", + "ᵥ": "v", + "ₓ": "x" +} \ No newline at end of file diff --git a/src/lib/file-parser.ts b/src/lib/file-parser.ts index e262b85bba..e3f4eb775e 100644 --- a/src/lib/file-parser.ts +++ b/src/lib/file-parser.ts @@ -118,8 +118,9 @@ export class FileParser { }).then((parserPromises) => { return Promise.all(parserPromises); }).then((data: ParsedDataWithFuzzy[]) => { - let localImagePromises: Promise<any>[] = []; - let localIconPromises: Promise<any>[] = []; + let defaultImagePromises: Promise<void>[] = []; + let localImagePromises: Promise<void>[] = []; + let localIconPromises: Promise<void>[] = []; let vParser = new VariableParser({ left: '${', right: '}' }); for (let i = 0; i < configs.length; i++) { @@ -127,7 +128,7 @@ export class FileParser { this.tryToReplaceTitlesWithVariables(data[i], configs[i], vParser); if (configs[i].fuzzyMatch.use) - this.fuzzyService.fuzzyMatcher.fuzzyMatchParsedData(data[i], configs[i].fuzzyMatch.removeCharacters, configs[i].fuzzyMatch.removeBrackets); + this.fuzzyService.fuzzyMatcher.fuzzyMatchParsedData(data[i], configs[i].fuzzyMatch); let userFilter = vParser.setInput(configs[i].userAccounts.specifiedAccounts).parse() ? _.uniq(vParser.extractVariables(data => null)) : []; let filteredAccounts = this.filterUserAccounts(steamDirectories[i].data, userFilter, configs[i].steamDirectory, configs[i].userAccounts.skipWithMissingDataDir); @@ -163,6 +164,8 @@ export class FileParser { startInDirectory: configs[i].startInDirectory.length > 0 ? configs[i].startInDirectory : path.dirname(executableLocation), argumentString: undefined, resolvedLocalImages: [], + resolvedDefaultImages: [], + defaultImage: undefined, localImages: [], resolvedLocalIcons: [], localIcons: [], @@ -179,27 +182,41 @@ export class FileParser { lastFile.finalTitle = vParser.setInput(configs[i].titleModifier).parse() ? vParser.replaceVariables((variable) => { return this.getVariable(variable as AllVariables, variableData).trim(); - }) : undefined; + }) : ''; variableData.finalTitle = lastFile.finalTitle; lastFile.argumentString = vParser.setInput(configs[i].executableArgs).parse() ? vParser.replaceVariables((variable) => { return this.getVariable(variable as AllVariables, variableData).trim(); - }) : undefined; + }) : ''; lastFile.imagePool = vParser.setInput(configs[i].imagePool).parse() ? vParser.replaceVariables((variable) => { return this.getVariable(variable as AllVariables, variableData).trim(); - }) : undefined; + }) : ''; lastFile.modifiedExecutableLocation = vParser.setInput(configs[i].executableModifier).parse() ? vParser.replaceVariables((variable) => { return this.getVariable(variable as AllVariables, variableData).trim(); - }) : undefined; + }) : ''; lastFile.onlineImageQueries = vParser.setInput(configs[i].onlineImageQueries).parse() ? _.uniq(vParser.extractVariables((variable) => { return this.getVariable(variable as AllVariables, variableData); - })) : undefined; + })) : []; lastFile.steamCategories = vParser.setInput(configs[i].steamCategory).parse() ? _.uniq(vParser.extractVariables((variable) => { return this.getVariable(variable as AllVariables, variableData); - })) : undefined; + })) : []; } + defaultImagePromises.push(this.resolveFieldGlobs('defaultImage', configs[i], parsedConfigs[i], vParser).then((data) => { + for (let j = 0; j < data.parsedConfig.files.length; j++) { + data.parsedConfig.files[j].resolvedDefaultImages = data.resolvedGlobs[j]; + + let extRegex = /png|tga|jpg|jpeg/i; + for (let k = 0; k < data.resolvedFiles[j].length; k++) { + const item = data.resolvedFiles[j][k]; + if (extRegex.test(path.extname(item))){ + data.parsedConfig.files[j].defaultImage = url.encodeFile(item); + break; + } + } + } + })); localImagePromises.push(this.resolveFieldGlobs('localImages', configs[i], parsedConfigs[i], vParser).then((data) => { for (let j = 0; j < data.parsedConfig.files.length; j++) { data.parsedConfig.files[j].resolvedLocalImages = data.resolvedGlobs[j]; @@ -219,7 +236,7 @@ export class FileParser { } })); } - return Promise.all(localImagePromises).then(() => Promise.all(localIconPromises)); + return Promise.all(localImagePromises).then(() => Promise.all(localIconPromises)).then(() => Promise.all(defaultImagePromises)); }).then(() => { return { parsedConfigs, noUserAccounts: totalUserAccountsFound === 0 }; }); @@ -340,7 +357,7 @@ export class FileParser { }).then((parsedData) => { for (let j = 0; j < parsedData.success.length; j++) { if (config.fuzzyMatch.use) { - if (this.fuzzyService.fuzzyMatcher.fuzzyMatchString(parsedData.success[j].extractedTitle, config.fuzzyMatch.removeCharacters, config.fuzzyMatch.removeBrackets) === parsedConfig.files[i].fuzzyTitle) { + if (this.fuzzyService.fuzzyMatcher.fuzzyMatchString(parsedData.success[j].extractedTitle, config.fuzzyMatch) === parsedConfig.files[i].fuzzyTitle) { resolvedFiles[i].push(parsedData.success[j].filePath); } } @@ -451,6 +468,12 @@ export class FileParser { break; } + match = /^rdc\|(.*)$/i.exec(output); + if (match) { + output = match[1].replaceDiacritics(); + break; + } + match = /^cv:?(.*)\|(.+)$/i.exec(output); if (match) { let groups = match[1] ? _.intersection(Object.keys(this.variableData), match[1]) : Object.keys(this.variableData); diff --git a/src/lib/file-selector.ts b/src/lib/file-selector.ts new file mode 100644 index 0000000000..7e219c61c9 --- /dev/null +++ b/src/lib/file-selector.ts @@ -0,0 +1,35 @@ +export class FileSelector { + private fileInput: HTMLInputElement = undefined; + private callback: (target: HTMLInputElement, event?: Event) => void = undefined; + + constructor() { + this.fileInput = document.createElement('input'); + this.fileInput.setAttribute('type', 'file'); + this.fileInput.onchange = (event: Event) => { + if (this.callback) + this.callback(this.fileInput, event); + + this.fileInput.value = null; + } + } + + set accept(value: string){ + this.fileInput.setAttribute('accept', value || ''); + } + + set multiple(value: boolean){ + this.fileInput.setAttribute('multiple', value ? 'true' : ''); + } + + set directory(value: boolean){ + this.fileInput.setAttribute('webkitdirectory', value ? 'true' : ''); + } + + set onChange(callback: (target: HTMLInputElement, event: Event) => void) { + this.callback = callback; + } + + trigger() { + this.fileInput.click(); + } +}; \ No newline at end of file diff --git a/src/lib/fuzzy-matcher.ts b/src/lib/fuzzy-matcher.ts index 355a5154ff..9e6c5e3da4 100644 --- a/src/lib/fuzzy-matcher.ts +++ b/src/lib/fuzzy-matcher.ts @@ -1,4 +1,4 @@ -import { ParsedDataWithFuzzy, FuzzyEventCallback } from "../models"; +import { ParsedDataWithFuzzy, FuzzyEventCallback, FuzzyMatcherOptions } from "../models"; import * as Fuzzy from 'fuzzaldrin-plus'; export class FuzzyMatcher { @@ -17,10 +17,10 @@ export class FuzzyMatcher { this.list = list; } - fuzzyMatchParsedData(data: ParsedDataWithFuzzy, removeCharacters: boolean, removeBrackets: boolean, verbose: boolean = true) { + fuzzyMatchParsedData(data: ParsedDataWithFuzzy, options: FuzzyMatcherOptions, verbose: boolean = true) { if (this.isLoaded()) { for (let i = 0; i < data.success.length; i++) { - let matchedData = this.matchFromList(data.success[i].extractedTitle, removeCharacters, removeBrackets); + let matchedData = this.matchFromList(data.success[i].extractedTitle, options); if (matchedData.matched) { data.success[i].fuzzyTitle = matchedData.output; @@ -32,9 +32,9 @@ export class FuzzyMatcher { return data; } - fuzzyMatchString(input: string, removeCharacters: boolean, removeBrackets: boolean, verbose: boolean = true) { + fuzzyMatchString(input: string, options: FuzzyMatcherOptions, verbose: boolean = true) { if (this.isLoaded()) { - let data = this.matchFromList(input, removeCharacters, removeBrackets); + let data = this.matchFromList(input, options); if (data.matched && verbose) this.eventCallback('info', { info: 'match', stringA: data.output, stringB: input }); return data.output; @@ -42,10 +42,10 @@ export class FuzzyMatcher { return input; } - fuzzyEqual(a: string, b: string, removeCharacters: boolean, removeBrackets: boolean, verbose: boolean = true) { + fuzzyEqual(a: string, b: string, options: FuzzyMatcherOptions, verbose: boolean = true) { if (this.isLoaded()) { - let dataA = this.matchFromList(a, removeCharacters, removeBrackets); - let dataB = this.matchFromList(b, removeCharacters, removeBrackets); + let dataA = this.matchFromList(a, options); + let dataB = this.matchFromList(b, options); if (dataA.output === dataB.output) { if (verbose) @@ -65,20 +65,20 @@ export class FuzzyMatcher { return this.list != null && this.list.games.length > 0; } - private matchFromList(input: string, removeCharacters: boolean, removeBrackets: boolean) { + private matchFromList(input: string, options: FuzzyMatcherOptions) { if (input.length === 0){ return { output: input, matched: false }; } // Check if title contains ", The..." else if (/,\s*the/i.test(input)) { let modifiedInput = input.replace(/(.*?),\s*(.*)/i, '$2 $1'); - modifiedInput = this.modifyString(modifiedInput, removeCharacters, removeBrackets); + modifiedInput = this.modifyString(modifiedInput, options); let matches = this.performMatching(modifiedInput); if (matches.matched) return matches; } - let modifiedInput = this.modifyString(input, removeCharacters, removeBrackets); + let modifiedInput = this.modifyString(input, options); return this.performMatching(modifiedInput); } @@ -90,16 +90,20 @@ export class FuzzyMatcher { return { output: input, matched: false }; } - private modifyString(input: string, removeCharacters: boolean, removeBrackets: boolean) { - if (removeCharacters) { + private modifyString(input: string, options: FuzzyMatcherOptions) { + if (options.replaceDiacritics){ + input = input.replaceDiacritics(); + } + + if (options.removeCharacters) { input = input.replace(/_/g, ' '); input = input.replace(/[^a-zA-Z0-9 \(\)\[\]]/g, ''); } - if (removeBrackets) + if (options.removeBrackets) input = input.replace(/\(.*?\)|\[.*?\]/g, ''); - if (removeCharacters || removeBrackets) { + if (options.removeCharacters || options.removeBrackets) { input = input.replace(/\s+/g, ' ').trim(); } diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index b2d2ec3e15..0fa7854a16 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -1,4 +1,4 @@ -import { ValidatorModifier, userAccountData, PreviewData, VDF_ListData, SteamTree, ParsedUserConfiguration, VDF_ShortcutsItem } from "../models"; +import { ValidatorModifier, userAccountData, PreviewData, VDF_ListData, SteamTree, ParsedUserConfiguration, VDF_ShortcutsItem, PreviewDataAppImage, AppImages } from "../models"; import { VDF_AddedItemsFile } from "./vdf-added-items-file"; import { VDF_ScreenshotsFile } from "./vdf-screenshots-file"; import { VDF_ShortcutsFile } from "./vdf-shortcuts-file"; @@ -469,4 +469,47 @@ export namespace file { return fs.copy(filepath, newFilepath, { overwrite: overwrite }).then(); } +} + +export namespace appImage { + export function getMaxLength(data: PreviewDataAppImage, images: AppImages) { + let imagesLength = images[data.imagePool] !== undefined ? images[data.imagePool].content.length : 0; + if (data.default !== undefined && imagesLength === 0) + imagesLength++; + if (data.steam !== undefined) + imagesLength++; + return imagesLength; + } + + export function setImageIndex(data: PreviewDataAppImage, images: AppImages, index: number) { + let currentIndex = data.imageIndex; + let length = getMaxLength(data, images); + if (index < 0) + index = length > 0 ? length - 1 : 0; + else if (index >= length) + index = 0; + + data.imageIndex = index; + } + + export function getCurrentImage(data: PreviewDataAppImage, images: AppImages) { + let imagesLength = images[data.imagePool] !== undefined ? images[data.imagePool].content.length : 0; + let length = getMaxLength(data, images); + + if (data.imageIndex !== 0 && data.imageIndex >= length) + setImageIndex(data, images, data.imageIndex); + + if (imagesLength === 0) { + if (data.imageIndex === 0 && data.steam !== undefined) + return data.steam; + else + return data.default || undefined; + } + else { + if (data.imageIndex === 0 && data.steam !== undefined) + return data.steam; + else + return images[data.imagePool].content[data.imageIndex - (data.steam === undefined ? 0 : 1)]; + } + } } \ No newline at end of file diff --git a/src/lib/image-providers/generic-provider.ts b/src/lib/image-providers/generic-provider.ts index 2729ba7ab8..ac385a14f6 100644 --- a/src/lib/image-providers/generic-provider.ts +++ b/src/lib/image-providers/generic-provider.ts @@ -1,3 +1,5 @@ +import '../replace-diacritics'; + import { FuzzyMatcher } from "../fuzzy-matcher"; import { FuzzyEventMap, ProviderPostEventMap, ProviderPostObject, ProviderReceiveEventMap, ImageContent } from "../../models"; diff --git a/src/lib/image-providers/retrogaming-cloud.worker.ts b/src/lib/image-providers/retrogaming-cloud.worker.ts index 6dc8e2e09b..e91b86215b 100644 --- a/src/lib/image-providers/retrogaming-cloud.worker.ts +++ b/src/lib/image-providers/retrogaming-cloud.worker.ts @@ -16,7 +16,7 @@ class RetrogamingCloudProvider extends GenericProvider { this.xrw.setSpecialErrors({ 404: { retryCount: 1, silent: true } }); let promises: Bluebird<void>[] = []; for (let i = 0; i < listData.length; i++) { - if (this.proxy.filter && listData[i].name && !this.proxy.fuzzyMatcher.fuzzyEqual(this.proxy.title, listData[i].name, true, true)) + if (this.proxy.filter && listData[i].name && !this.proxy.fuzzyMatcher.fuzzyEqual(this.proxy.title, listData[i].name, { removeBrackets: true, removeCharacters: true, replaceDiacritics: true })) continue; else { if (listData[i].id !== undefined) diff --git a/src/lib/index.ts b/src/lib/index.ts index b006e94137..fc005d98b7 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -12,4 +12,5 @@ export * from './language-manager'; export * from './vdf-shortcuts-file'; export * from './vdf-screenshots-file'; export * from './vdf-added-items-file'; -export * from './vdf-error'; \ No newline at end of file +export * from './vdf-error'; +export * from './file-selector'; \ No newline at end of file diff --git a/src/lib/markdown-variable.ts b/src/lib/markdown-variable.ts index 4f24e57d58..ec3a6289fd 100644 --- a/src/lib/markdown-variable.ts +++ b/src/lib/markdown-variable.ts @@ -15,7 +15,7 @@ export function MarkdownVariable(md: MarkdownIt, options?: any) { if (endPos !== -1) { let content = state.src.slice(state.pos, ++endPos); if (!content.match(/\r\n|\r|\n/g)) { - let match = /(svg|APP|DOM)\s*\[(.+)\]/.exec(content); + let match = /(APP)\s*\[(.+)\]/.exec(content); if (!silent && match !== null && match[1] != null && match[2] != null) { let token = state.push("markdown-variable", match[1], 0); token.content = match[2]; @@ -36,12 +36,8 @@ export function MarkdownVariable(md: MarkdownIt, options?: any) { md.renderer.rules["markdown-variable"] = (tokens, idx, options, env, self) => { const token = tokens[idx]; switch (token.tag) { - case 'svg': - return `<svg ${md.utils.escapeHtml(tokens[idx].content)}></svg>`; case 'APP': return `${_.get(APP, md.utils.escapeHtml(tokens[idx].content), 'undefined')}`; - case 'DOM': - return tokens[idx].content; default: return ''; } diff --git a/src/lib/replace-diacritics.ts b/src/lib/replace-diacritics.ts new file mode 100644 index 0000000000..c392169503 --- /dev/null +++ b/src/lib/replace-diacritics.ts @@ -0,0 +1,17 @@ +const diacriticList = require('./diacritic.json'); + +declare global { + interface String { + replaceDiacritics(): string; + } +} + +String.prototype.replaceDiacritics = function () { + return (this as String).replace(/[^A-Za-z0-9 ]/g, + function (char) { + return diacriticList[char] || char; + } + ); +} + +export default undefined; \ No newline at end of file diff --git a/src/lib/vdf-manager.ts b/src/lib/vdf-manager.ts index 6fe54d5c3f..1af876f43c 100644 --- a/src/lib/vdf-manager.ts +++ b/src/lib/vdf-manager.ts @@ -1,6 +1,6 @@ import { VDF_ListData, SteamDirectory, PreviewData, AppImages, VDF_ListItem } from "../models"; import { VDF_Error } from './vdf-error'; -import { vdf } from './helpers'; +import { vdf, appImage } from './helpers'; import { APP } from '../variables'; import * as _ from 'lodash'; @@ -125,8 +125,7 @@ export class VDF_Manager { if (app.status === 'add') { let item = listItem.shortcuts.getItem(appId); - let imageIndex = app.currentImageIndex; - let appImages = images[app.imagePool].content; + let currentImage = appImage.getCurrentImage(app.images, images); if (item !== undefined) { item.appname = app.title; @@ -149,8 +148,8 @@ export class VDF_Manager { listItem.addedItems.addItem(appId); - if (appImages.length > 0 && imageIndex !== -1 && appImages[imageIndex] !== undefined) { - listItem.screenshots.addItem({ appId, title: app.title, url: appImages[imageIndex].imageUrl }); + if (currentImage !== undefined && currentImage.imageProvider !== 'Steam') { + listItem.screenshots.addItem({ appId, title: app.title, url: currentImage.imageUrl }); } } else if (app.status === 'remove') { @@ -158,9 +157,7 @@ export class VDF_Manager { listItem.addedItems.removeItem(appId); listItem.screenshots.removeItem(appId); - app.steamImage = undefined; - if (app.currentImageIndex === -1) - app.currentImageIndex = 0; + app.images.steam = undefined } } }); diff --git a/src/models/fuzzy.model.ts b/src/models/fuzzy.model.ts index 671c1f281c..a007bd179d 100644 --- a/src/models/fuzzy.model.ts +++ b/src/models/fuzzy.model.ts @@ -46,4 +46,10 @@ export interface ParsedDataWithFuzzy { fuzzyTitle: string }[], failed: string[] +} + +export interface FuzzyMatcherOptions { + removeCharacters?: boolean, + removeBrackets?: boolean, + replaceDiacritics?: boolean } \ No newline at end of file diff --git a/src/models/language.model.ts b/src/models/language.model.ts index cc04e85dc0..68c2562b27 100644 --- a/src/models/language.model.ts +++ b/src/models/language.model.ts @@ -195,6 +195,7 @@ export interface languageStruct { onlineImageQueries: string[], imageProviders: string[], imagePool: string[], + defaultImage: string[], localImages: string[], localIcons: string[] }, @@ -227,6 +228,9 @@ export interface languageStruct { completeShortcut__i: string, //${index}, ${total}, ${shortcut} firstImageQuery__i: string, //${index}, ${total}, ${query} imageQueries__i: string, //${index}, ${total}, ${query} + resolvedDefaultImageGlob__i: string, //${index}, ${total} + resolvedDefaultImageGlobInfo__i: string, //${index}, ${total}, ${glob} + defaultImageResolved__i: string, //${index}, ${total}, ${image} resolvedImageGlob__i: string, //${index}, ${total} resolvedImageGlobInfo__i: string, //${index}, ${total}, ${glob} localImagesResolved__i: string, //${index}, ${total} @@ -253,6 +257,7 @@ export interface languageStruct { onlineImageQueries: string, imageProviders: string, imagePool: string, + defaultImage: string, localImages: string, localIcons: string }, @@ -269,6 +274,7 @@ export interface languageStruct { fuzzy_use: string, fuzzy_removeCharacters: string, fuzzy_removeBrackets: string, + fuzzy_replaceDiacritic: string, appendArgsToExecutable: string, disabled: string, advanced: string, diff --git a/src/models/parser.model.ts b/src/models/parser.model.ts index 6945f8360b..c1b38d7cfc 100644 --- a/src/models/parser.model.ts +++ b/src/models/parser.model.ts @@ -14,6 +14,8 @@ export interface ParsedUserConfigurationFile { onlineImageQueries: string[], steamCategories: string[], imagePool: string, + resolvedDefaultImages: string[], + defaultImage: string, localImages: string[], localIcons: string[] } diff --git a/src/models/preview.model.ts b/src/models/preview.model.ts index 6f7962c4a7..db3f574287 100644 --- a/src/models/preview.model.ts +++ b/src/models/preview.model.ts @@ -20,6 +20,13 @@ export interface AppImages { [extractedTitle: string]: ImagesStatusAndContent }; +export interface PreviewDataAppImage { + steam: ImageContent, // 0? index + default: ImageContent, // 0-1? index + imagePool: string, // 0-2+ index + imageIndex: number +} + export interface PreviewDataApp { entryId: number, status: 'add' | 'skip' | 'remove', @@ -30,11 +37,9 @@ export interface PreviewDataApp { executableLocation: string, title: string, argumentString: string, - steamImage: ImageContent, - currentImageIndex: number, currentIconIndex: number, icons: string[], - imagePool: string + images: PreviewDataAppImage } export interface PreviewDataApps { diff --git a/src/models/user-configuration.model.ts b/src/models/user-configuration.model.ts index e2cc9dae0a..95f4d6ce1d 100644 --- a/src/models/user-configuration.model.ts +++ b/src/models/user-configuration.model.ts @@ -22,13 +22,15 @@ export interface UserConfiguration { fuzzyMatch: { use: boolean, removeCharacters: boolean, - removeBrackets: boolean + removeBrackets: boolean, + replaceDiacritics: boolean }, onlineImageQueries: string, imageProviders: string[], executableArgs: string, imagePool: string, appendArgsToExecutable: boolean, + defaultImage: string, localImages: string, localIcons: string, titleModifier: string, diff --git a/src/renderer/app.module.ts b/src/renderer/app.module.ts index 2345b14de7..1add53b415 100644 --- a/src/renderer/app.module.ts +++ b/src/renderer/app.module.ts @@ -5,7 +5,6 @@ import { HttpModule } from '@angular/http'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DatePipe, APP_BASE_HREF } from '@angular/common'; import { ColorPickerModule } from 'ngx-color-picker'; -import { DynamicHTMLModule } from 'ng-dynamic'; import * as Components from './components'; import * as SvgComponents from './svg-components'; @@ -15,30 +14,13 @@ import * as Pipes from './pipes'; import * as Guards from './guards'; import { AppRoutes } from './app.routing'; -function ngObjectsToArray(importObject: any, selector: boolean = false) { - if (selector === true) { - let objectArray: { component: any, selector: string }[] = []; - for (let attribute in importObject) { - if (typeof importObject[attribute] === 'function') { - let metadata = Reflect.getMetadata('annotations', importObject[attribute]); - for (let i = 0; i < metadata.length; i++) { - if (metadata[i].selector) { - objectArray.push({ component: importObject[attribute], selector: metadata[i].selector }); - break; - } - } - } - } - return objectArray; - } - else { - let objectArray: any[] = []; - for (let attribute in importObject) { - if (typeof importObject[attribute] === 'function') - objectArray.push(importObject[attribute]); - } - return objectArray; +function ngObjectsToArray(importObject: any) { + let objectArray: any[] = []; + for (let attribute in importObject) { + if (typeof importObject[attribute] === 'function') + objectArray.push(importObject[attribute]); } + return objectArray; } @NgModule({ @@ -49,12 +31,7 @@ function ngObjectsToArray(importObject: any, selector: boolean = false) { AppRoutes, FormsModule, ReactiveFormsModule, - ColorPickerModule, - DynamicHTMLModule.forRoot({ - components: [].concat( - ngObjectsToArray(SvgComponents, true) - ) - }) + ColorPickerModule ], declarations: [].concat( ngObjectsToArray(Components), diff --git a/src/renderer/app.ts b/src/renderer/app.ts index f6fabbbe2c..0b251bd865 100644 --- a/src/renderer/app.ts +++ b/src/renderer/app.ts @@ -1,6 +1,7 @@ import 'zone.js/dist/zone'; import 'reflect-metadata'; import '../lib/string-interpolation'; +import '../lib/replace-diacritics'; import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; diff --git a/src/renderer/components/app.component.ts b/src/renderer/components/app.component.ts index cd7befa7c7..a772f37414 100644 --- a/src/renderer/components/app.component.ts +++ b/src/renderer/components/app.component.ts @@ -39,6 +39,7 @@ export class AppComponent { } }); this.markdownService.createInstance('default', new markdownIt({ + html: true, typographer: true, highlight: function (str, lang) { if (lang && highlight.getLanguage(lang)) { diff --git a/src/renderer/components/markdown.component.ts b/src/renderer/components/markdown.component.ts index ed122566eb..7989d6791a 100644 --- a/src/renderer/components/markdown.component.ts +++ b/src/renderer/components/markdown.component.ts @@ -1,5 +1,4 @@ -import { Component, ElementRef, Input, SimpleChanges, OnChanges, OnDestroy, DoCheck, ViewEncapsulation } from '@angular/core'; -import { DynamicHTMLRenderer, DynamicHTMLRef } from 'ng-dynamic'; +import { Component, ElementRef, Input, SimpleChanges, OnChanges, ViewEncapsulation, Renderer2 } from '@angular/core'; import { MarkdownService } from '../services'; @Component({ @@ -10,30 +9,12 @@ import { MarkdownService } from '../services'; }) export class MarkdownComponent { @Input() content: string; - private ref: DynamicHTMLRef = null; - constructor(private renderer: DynamicHTMLRenderer, private elementRef: ElementRef, private markdownService: MarkdownService) { } + constructor(private renderer: Renderer2, private elementRef: ElementRef, private markdownService: MarkdownService) { } ngOnChanges(_: SimpleChanges) { - if (this.ref) { - this.ref.destroy(); - this.ref = null; - } if (this.content && this.elementRef) { - this.ref = this.renderer.renderInnerHTML(this.elementRef, this.markdownService.compile(this.content)); - } - } - - ngDoCheck() { - if (this.ref) { - this.ref.check(); - } - } - - ngOnDestroy() { - if (this.ref) { - this.ref.destroy(); - this.ref = null; + this.renderer.setProperty(this.elementRef.nativeElement, 'innerHTML', this.markdownService.compile(this.content)); } } } \ No newline at end of file diff --git a/src/renderer/components/parsers.component.ts b/src/renderer/components/parsers.component.ts index ac3d579394..f40f45cd24 100644 --- a/src/renderer/components/parsers.component.ts +++ b/src/renderer/components/parsers.component.ts @@ -243,6 +243,9 @@ export class ParsersComponent implements AfterViewInit, OnDestroy { use: new NestedFormElement.Toggle({ text: this.lang.text.fuzzy_use }), + replaceDiacritics: new NestedFormElement.Toggle({ + text: this.lang.text.fuzzy_replaceDiacritic + }), removeCharacters: new NestedFormElement.Toggle({ text: this.lang.text.fuzzy_removeCharacters }), @@ -300,6 +303,16 @@ export class ParsersComponent implements AfterViewInit, OnDestroy { this.currentDoc.content = this.lang.docs__md.imageProviders.join(''); } }), + defaultImage: new NestedFormElement.Input({ + isHidden: () => this.isHiddenMode(), + highlight: this.highlight.bind(this), + label: this.lang.label.defaultImage, + onValidate: (self, path) => this.parsersService.validate(path[0] as keyof UserConfiguration, self.value), + onInfoClick: (self, path) => { + this.currentDoc.activePath = path.join(); + this.currentDoc.content = this.lang.docs__md.defaultImage.join(''); + } + }), localImages: new NestedFormElement.Input({ isHidden: () => this.isHiddenMode(), highlight: this.highlight.bind(this), @@ -588,6 +601,26 @@ export class ParsersComponent implements AfterViewInit, OnDestroy { })); } } + if (data.files[i].resolvedDefaultImages.length) { + success(this.lang.success.resolvedDefaultImageGlob__i.interpolate({ + index: i + 1, + total: totalLength + })); + for (let j = 0; j < data.files[i].resolvedDefaultImages.length; j++) { + success(this.lang.success.resolvedImageGlobInfo__i.interpolate({ + index: i + 1, + total: totalLength, + glob: data.files[i].resolvedDefaultImages[j] + })); + } + } + if (data.files[i].defaultImage !== undefined) { + success(this.lang.success.defaultImageResolved__i.interpolate({ + index: i + 1, + total: totalLength, + image: data.files[i].defaultImage + })); + } if (data.files[i].resolvedLocalImages.length) { success(this.lang.success.resolvedImageGlob__i.interpolate({ index: i + 1, diff --git a/src/renderer/components/preview.component.ts b/src/renderer/components/preview.component.ts index 9032fb57b1..88d55a9e5c 100644 --- a/src/renderer/components/preview.component.ts +++ b/src/renderer/components/preview.component.ts @@ -3,7 +3,7 @@ import { Subscription } from 'rxjs'; import { PreviewService, SettingsService, ImageProviderService } from "../services"; import { PreviewData, PreviewDataApp, PreviewVariables, AppSettings, ImageContent } from "../../models"; import { APP } from '../../variables'; -import { url } from '../../lib'; +import { url, appImage, FileSelector } from '../../lib'; import * as _ from 'lodash'; import * as path from 'path'; @@ -20,6 +20,7 @@ export class PreviewComponent implements OnDestroy { private previewVariables: PreviewVariables; private filterValue: string = ''; private scrollingEntries: boolean = false; + private fileSelector: FileSelector = new FileSelector(); constructor(private previewService: PreviewService, private settingsService: SettingsService, private imageProviderService: ImageProviderService, private changeDetectionRef: ChangeDetectorRef, private renderer: Renderer2, private elementRef: ElementRef) { this.previewData = this.previewService.getPreviewData(); @@ -47,13 +48,17 @@ export class PreviewComponent implements OnDestroy { this.subscriptions.unsubscribe(); } - getImagePool(poolKey: string) { + private getImagePool(poolKey: string) { return this.previewService.images[poolKey]; } - private getBackgroundImage(app: PreviewDataApp, image: ImageContent) { + private getBackgroundImage(app: PreviewDataApp) { + return this.previewService.getCurrentImage(app); + } + + private setBackgroundImage(app: PreviewDataApp, image: ImageContent) { if (image == undefined) { - if (this.previewService.images[app.imagePool].retrieving) + if (this.previewService.images[app.images.imagePool].retrieving) return require('../../assets/images/retrieving-images.svg'); else return require('../../assets/images/no-images.svg'); @@ -76,44 +81,37 @@ export class PreviewComponent implements OnDestroy { this.previewService.loadImage(app); } - private getItemClass(app: PreviewDataApp, image: ImageContent) { - return { - retrieving: this.previewService.images[app.imagePool].retrieving, - noImages: !this.previewService.images[app.imagePool].retrieving && image == undefined, - downloading: image != undefined && image.loadStatus === 'downloading', - failed: image != undefined && image.loadStatus === 'failed', - imageLoaded: image != undefined && image.loadStatus === 'done' - } - } - private areImagesAvailable(app: PreviewDataApp) { - return this.previewService.images[app.imagePool].content.length > (app.steamImage ? 0 : 1); + return this.previewService.areImagesAvailable(app); } private currentImageIndex(app: PreviewDataApp) { - return app.currentImageIndex + (app.steamImage ? 2 : 1); + return app.images.imageIndex + 1; } private maxImageIndex(app: PreviewDataApp) { - return this.getImagePool(app.imagePool).content.length + (app.steamImage ? 1 : 0); - } - - private addLocalImages(app: PreviewDataApp, event: Event) { - let target = event.target as HTMLInputElement; - if (target.files) { - let extRegex = /png|tga|jpg|jpeg/i; - for (let i = 0; i < target.files.length; i++) { - if (extRegex.test(path.extname(target.files[i].path))) { - let imageUrl = url.encodeFile(target.files[i].path); - this.previewService.addUniqueImage(app.imagePool, { - imageProvider: 'LocalStorage', - imageUrl, - loadStatus: 'done' - }); + return this.previewService.getTotalLengthOfImages(app); + } + + private addLocalImages(app: PreviewDataApp) { + this.fileSelector.multiple = true; + this.fileSelector.accept = '.png, .jpeg, .jpg, .tga'; + this.fileSelector.onChange = (target) => { + if (target.files) { + let extRegex = /png|tga|jpg|jpeg/i; + for (let i = 0; i < target.files.length; i++) { + if (extRegex.test(path.extname(target.files[i].path))) { + let imageUrl = url.encodeFile(target.files[i].path); + this.previewService.addUniqueImage(app.images.imagePool, { + imageProvider: 'LocalStorage', + imageUrl, + loadStatus: 'done' + }); + } } } - target.value = null; - } + }; + this.fileSelector.trigger(); } private get lang() { @@ -148,15 +146,15 @@ export class PreviewComponent implements OnDestroy { } private refreshImages(app: PreviewDataApp) { - this.previewService.downloadImageUrls([app.imagePool], app.imageProviders); + this.previewService.downloadImageUrls([app.images.imagePool], app.imageProviders); } private previousImage(app: PreviewDataApp) { - this.previewService.setImageIndex(app, app.currentImageIndex - 1); + this.previewService.setImageIndex(app, app.images.imageIndex - 1); } private nextImage(app: PreviewDataApp) { - this.previewService.setImageIndex(app, app.currentImageIndex + 1); + this.previewService.setImageIndex(app, app.images.imageIndex + 1); } private previousIcon(app: PreviewDataApp) { diff --git a/src/renderer/schemas/user-configuration.schema.ts b/src/renderer/schemas/user-configuration.schema.ts index 430559df8f..48ae2a456b 100644 --- a/src/renderer/schemas/user-configuration.schema.ts +++ b/src/renderer/schemas/user-configuration.schema.ts @@ -45,6 +45,7 @@ export const userConfiguration = { executableArgs: { type: 'string', default: '' }, appendArgsToExecutable: { type: 'boolean', default: true }, imagePool: { type: 'string', default: '${fuzzyTitle}' }, + defaultImage: { type: 'string', default: '' }, localImages: { type: 'string', default: '' }, localIcons: { type: 'string', default: '' }, onlineImageQueries: { type: 'string', default: '${${fuzzyTitle}}' }, @@ -67,7 +68,8 @@ export const userConfiguration = { properties: { use: { type: 'boolean', default: true }, removeCharacters: { type: 'boolean', default: true }, - removeBrackets: { type: 'boolean', default: true } + removeBrackets: { type: 'boolean', default: true }, + replaceDiacritics: { type: 'boolean', default: true } } }, advanced: { type: 'boolean', default: false }, diff --git a/src/renderer/services/parsers.service.ts b/src/renderer/services/parsers.service.ts index 4a22f54d31..83c62f6d39 100644 --- a/src/renderer/services/parsers.service.ts +++ b/src/renderer/services/parsers.service.ts @@ -210,6 +210,7 @@ export class ParsersService { return _.isArray(data) ? null : this.lang.validationErrors.imageProviders__md; case 'imagePool': return this.validateVariableParserString(data || '', this.lang.validationErrors.imagePool__md); + case 'defaultImage': case 'localImages': case 'localIcons': return this.fileParser.validateFieldGlob(data || ''); @@ -246,7 +247,7 @@ export class ParsersService { 'steamDirectory', 'startInDirectory', 'specifiedAccounts', 'titleFromVariable', 'titleModifier', 'executableArgs', 'onlineImageQueries', 'imagePool', 'imageProviders', - 'localImages', 'localIcons' + 'defaultImage', 'localImages', 'localIcons' ]; for (let i = 0; i < simpleValidations.length; i++) { diff --git a/src/renderer/services/preview.service.ts b/src/renderer/services/preview.service.ts index 00688ca896..b3e74b66b1 100644 --- a/src/renderer/services/preview.service.ts +++ b/src/renderer/services/preview.service.ts @@ -10,7 +10,7 @@ import { ImagesStatusAndContent, ProviderCallbackEventMap, PreviewDataApp, AppSettings, SteamTree, userAccountData } from '../../models'; -import { VDF_Manager, VDF_Error, steam, url } from "../../lib"; +import { VDF_Manager, VDF_Error, steam, url, appImage } from "../../lib"; import { APP } from '../../variables'; import { queue } from 'async'; import * as _ from "lodash"; @@ -167,8 +167,8 @@ export class PreviewService { loadImage(app: PreviewDataApp) { if (app) { - let image = this.appImages[app.imagePool].content[app.currentImageIndex]; - if (image && (image.loadStatus === 'notStarted' || image.loadStatus === 'failed')) { + let image = appImage.getCurrentImage(app.images, this.appImages); + if (image !== undefined && (image.loadStatus === 'notStarted' || image.loadStatus === 'failed')) { if (image.loadStatus === 'failed') { this.loggerService.info(this.lang.info.retryingDownload__i.interpolate({ imageUrl: image.imageUrl, @@ -225,15 +225,26 @@ export class PreviewService { setImageIndex(app: PreviewDataApp, index: number) { if (app) { - let images = this.appImages[app.imagePool].content; - if (images.length) { - let minIndex = app.steamImage ? -1 : 0; - app.currentImageIndex = index < minIndex ? images.length - 1 : (index < images.length ? index : minIndex); - this.previewDataChanged.next(); - } + appImage.setImageIndex(app.images, this.appImages, index); + this.previewDataChanged.next(); } } + areImagesAvailable(app: PreviewDataApp) { + return this.getTotalLengthOfImages(app) > 0; + } + + getTotalLengthOfImages(app: PreviewDataApp) { + if (app) + return appImage.getMaxLength(app.images, this.appImages); + else + return 0; + } + + getCurrentImage(app: PreviewDataApp) { + return appImage.getCurrentImage(app.images, this.appImages); + } + setIconIndex(app: PreviewDataApp, index: number) { if (app && app.icons.length) { app.currentIconIndex = index < 0 ? app.icons.length - 1 : (index < app.icons.length ? index : 0); @@ -433,12 +444,8 @@ export class PreviewService { if (previewData[config.steamDirectory][userAccount.accountID].apps[appID] === undefined) { let steamImage = gridData[config.steamDirectory][userAccount.accountID][appID]; let steamImageUrl = steamImage ? url.encodeFile(steamImage) : undefined; - let currentImageIndex = oldDataApp !== undefined ? oldDataApp.currentImageIndex : steamImageUrl ? -1 : 0; let currentIconIndex = oldDataApp !== undefined ? oldDataApp.currentIconIndex : 0; - if (steamImageUrl ? -1 : 0 > currentImageIndex || currentImageIndex > this.appImages[file.imagePool].content.length) - currentImageIndex = steamImageUrl ? -1 : 0; - if (0 > currentIconIndex || currentIconIndex > file.localIcons.length) currentIconIndex = 0; @@ -451,16 +458,23 @@ export class PreviewService { imageProviders: config.imageProviders, argumentString: config.appendArgsToExecutable ? '' : file.argumentString, title: file.finalTitle, - steamImage: steamImage ? { - imageProvider: 'Steam', - imageUrl: steamImageUrl, - loadStatus: 'done' - } : undefined, - currentImageIndex, + images: { + steam: steamImage ? { + imageProvider: 'Steam', + imageUrl: steamImageUrl, + loadStatus: 'done' + } : undefined, + default: file.defaultImage ? { + imageProvider: 'LocalStorage', + imageUrl: file.defaultImage, + loadStatus: 'done' + } : undefined, + imagePool: file.imagePool, + imageIndex: 0 + }, executableLocation, currentIconIndex, - icons: file.localIcons, - imagePool: file.imagePool + icons: file.localIcons }; } else { diff --git a/src/renderer/templates/preview.component.html b/src/renderer/templates/preview.component.html index 92c6eec869..3d0d4f2c7e 100644 --- a/src/renderer/templates/preview.component.html +++ b/src/renderer/templates/preview.component.html @@ -23,9 +23,9 @@ <div class="apps"> <ng-container *ngFor="let appID of previewData[steamDir][steamUser].apps | keys"> <ng-container *ngVar="previewData[steamDir][steamUser].apps[appID] as app"> - <ng-container *ngVar="(app.currentImageIndex === -1 ? app.steamImage : getImagePool(app.imagePool).content[app.currentImageIndex]) as image"> - <div class="app" *ngIf="(app.title | fuzzyTest: filterValue)" [style.backgroundImage]="getBackgroundImage(app, image) | cssUrl | safeStyle" - [class.retrieving]="getImagePool(app.imagePool).retrieving" [class.noImages]="!this.previewService.images[app.imagePool].retrieving && image == undefined" + <ng-container *ngVar="getBackgroundImage(app) as image"> + <div class="app" *ngIf="(app.title | fuzzyTest: filterValue)" [style.backgroundImage]="setBackgroundImage(app, image) | cssUrl | safeStyle" + [class.retrieving]="getImagePool(app.images.imagePool).retrieving" [class.noImages]="!getImagePool(app.images.imagePool).retrieving && image == undefined" [class.downloading]="image != undefined && image.loadStatus === 'downloading'" [class.failed]="image != undefined && image.loadStatus === 'failed'" [class.imageLoaded]="image != undefined && image.loadStatus === 'done'" (mouseenter)="entries.hoverIndex = app.entryId" (mouseleave)="entries.hoverIndex = undefined"> @@ -56,10 +56,9 @@ {{app.configurationTitle + (app.steamCategories.length > 0 ? ' (' + app.steamCategories.join(', ') + ')' : '')}} </div> <div class="appButtons"> - <svg class="button" [title]="lang.addLocalImages" add-images hover="true" active="true" (click)="fileInput.click()"></svg> - <input style="display: none;" #fileInput multiple type="file" accept=".png, .jpeg, .jpg, .tga" (change)="addLocalImages(app, $event)"/> + <svg class="button" [title]="lang.addLocalImages" add-images hover="true" active="true" (click)="addLocalImages(app)"></svg> <span class="separator"></span> - <svg class="button" *ngIf="!getImagePool(app.imagePool).retrieving" (click)="refreshImages(app)" [title]="lang.refreshImages" refresh-images hover="true" active="true"></svg> + <svg class="button" *ngIf="!getImagePool(app.images.imagePool).retrieving" (click)="refreshImages(app)" [title]="lang.refreshImages" refresh-images hover="true" active="true"></svg> <svg class="button" *ngIf="image != undefined && image.loadStatus === 'failed'" (click)="loadImage(app)" [title]="lang.retryDownload" image-alert hover="true" active="true"></svg> </div> </div> diff --git a/webpack/renderer.js b/webpack/renderer.js index b2bc815683..31be28baef 100644 --- a/webpack/renderer.js +++ b/webpack/renderer.js @@ -76,7 +76,7 @@ let clientConfig = { template: helpers.root('src', 'renderer', 'index.html') }), new webpack.ContextReplacementPlugin( - /angular(\\|\/)core(\\|\/)@angular/, + /\@angular(\\|\/)core(\\|\/)esm5/, helpers.root('dist') ), GlobalStyle