Compare commits
273 Commits
6.0.1-chro
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 78395e3ea0 | |||
| e269ff3d79 | |||
| be495b1c56 | |||
| 3f032619b4 | |||
| ef5ba706b7 | |||
| 3b91c2224c | |||
| 822d069b82 | |||
| 8af9f2cedf | |||
| 3d8d8eb199 | |||
| a0d599ce2f | |||
| fd7b5ec04e | |||
| c9818c92b3 | |||
| f57163f2bb | |||
| dbdcb4e367 | |||
| e1dd0f9c04 | |||
| 2cb6a88c5e | |||
| eb89ba03db | |||
| cb02c8619d | |||
| 7d296e6a3d | |||
| 35bb5b477b | |||
| 925a9fdea3 | |||
| f0baa6ad67 | |||
| f25749470b | |||
| 8a4985e6a3 | |||
| 2cce60fb9f | |||
| 826acf9a3f | |||
| 186de15781 | |||
| 17dc69f87a | |||
| 8268c7a7af | |||
| a71b99ff10 | |||
| 54c4f49edb | |||
| 92cd1cf58b | |||
| 64570c71ce | |||
| cbc4b7877f | |||
| b62f6feaea | |||
| b6dec385dc | |||
| a12581ed0e | |||
| e651e51c50 | |||
| 56321e52fc | |||
| 73eec1f7ba | |||
| a0cddb256a | |||
| 14b3571af3 | |||
| af08c5094d | |||
| ef12dc8cc5 | |||
| fcfc0361dc | |||
| a8847bdeff | |||
| 3f5ab5ecc6 | |||
| 077c7cae15 | |||
| f916cffd41 | |||
| ea4fb95a3c | |||
| 3dbec85269 | |||
| 2a42c3e715 | |||
| c2a6a3f082 | |||
| 61972a7b2d | |||
| 4a562443b1 | |||
| 9515c1b01a | |||
| 126d891e5e | |||
| dad1df0244 | |||
| aff429e5ea | |||
| 3457c4fcdc | |||
| 74c0358d33 | |||
| 8735d3e81a | |||
| 4180d5b5b1 | |||
| ca751a660f | |||
| fb7f5cf1ba | |||
| fe2f52d76a | |||
| ee32ae93c3 | |||
| 877fc96da1 | |||
| 67a5b4e05a | |||
| d0a5083841 | |||
| 2d8c078a6f | |||
| f543dcff24 | |||
| 5cfa5b0add | |||
| 13feb1e822 | |||
| dc755dbe62 | |||
| b334707463 | |||
| ca093f5b34 | |||
| 2e4226b828 | |||
| 095a3daadd | |||
| baa7c79f6c | |||
| f5e308c5b4 | |||
| c0c29759fa | |||
| ddffbe92a0 | |||
| 888e48eb5b | |||
| 61e7351d0b | |||
| 4b0571e123 | |||
| bf7f707ab7 | |||
| accc320971 | |||
| 4276a9f03a | |||
| 7ae3fb6109 | |||
| 8c96e2fd90 | |||
| 30e5bce38f | |||
| 620028db6d | |||
| 850b5d6b25 | |||
| eaf003f5a7 | |||
| ab1efbe9cf | |||
| 14f8453067 | |||
| 090f112463 | |||
| 01d569f1b5 | |||
| 75670ef3f9 | |||
| f88f703e84 | |||
| a5fdef0196 | |||
| f10eee335d | |||
| 20856891b6 | |||
| 60dd1193ab | |||
| 2bc95af73c | |||
| 178bb65b4a | |||
| f7f046ea53 | |||
| ec0896e17a | |||
| a5f35248bd | |||
| a05eccce9e | |||
| e118777636 | |||
| 7998e8158e | |||
| 14f837d1f0 | |||
| cff1d3cf16 | |||
| b2cd96982e | |||
| a89eb8e857 | |||
| 7c1ccd2cc9 | |||
| e3a12bc601 | |||
| e20508956a | |||
| 4000d0e027 | |||
| 77fcced776 | |||
| e67b5a227f | |||
| 150a0a8a90 | |||
| 269dddc92e | |||
| 508ef5cbbb | |||
| 500d06204c | |||
| fe8436179f | |||
| adedfec1d0 | |||
| b974111eb4 | |||
| 3d7b50a2e3 | |||
| 32c11d1f61 | |||
| 5c48747d0f | |||
| 5a0abb3595 | |||
| 95b98c9053 | |||
| 518e09e3cb | |||
| ad632b0680 | |||
| 73aa925067 | |||
| 391b0ac7ab | |||
| ae2bb7afcd | |||
| 0681c3ae08 | |||
| 4aa96bb288 | |||
| b24739d70d | |||
| dffe8e055a | |||
| adadc8bb3e | |||
| 5f3562fe5b | |||
| 0d7c535a70 | |||
| 39f39f23a7 | |||
| ff0de6f2e7 | |||
| 8658b3c0d3 | |||
| 8551bc2dc1 | |||
| a0abf336cf | |||
| 7d71dd0507 | |||
| 54c190f86c | |||
| e198c69741 | |||
| 7c1d3e52e5 | |||
| 5460719297 | |||
| 83b770e4e8 | |||
| a5c9835d9d | |||
| f0840b78e1 | |||
| ab79f1e0e6 | |||
| 16a2f511bb | |||
| 39fdd72e9f | |||
| f93a7e284c | |||
| b5e480a486 | |||
| bf128babcc | |||
| 46d1ad221f | |||
| 7aacbec108 | |||
| f77506c454 | |||
| e3f1f21ce9 | |||
| 08c86615a0 | |||
| b0425b37c0 | |||
| d94be7aa5e | |||
| 40bdddd031 | |||
| da94dee780 | |||
| 587a21c5f6 | |||
| f994617d44 | |||
| a92f5fc2a1 | |||
| 208a360c47 | |||
| f728cbf817 | |||
| aef5665cb5 | |||
| 6ea47d26d0 | |||
| a7a36033a2 | |||
| 32f4d99c98 | |||
| 2d08a2f339 | |||
| 1f0ebff241 | |||
| 5c26e49c17 | |||
| ea85f2f5d6 | |||
| af9b123e8d | |||
| d322c0909a | |||
| 0bfe84d73d | |||
| 244e8227e7 | |||
| 5da8073235 | |||
| 5479b7ee95 | |||
| b4e932581c | |||
| 1f65cca002 | |||
| 1a9cdfa93b | |||
| d780a8cb12 | |||
| eaf619f4f8 | |||
| 3dd8584954 | |||
| ebdc26a00d | |||
| dd47dd09da | |||
| bc6d2ac549 | |||
| b2cd8197d4 | |||
| 23ea9e77c4 | |||
| 42ac51f1e9 | |||
| d433878680 | |||
| 11fa12001c | |||
| a82a6963e9 | |||
| a98a80001c | |||
| ad1e73d0cc | |||
| 55c0e7e8c5 | |||
| 3d35ae32c8 | |||
| 33e3bec337 | |||
| 9c12272846 | |||
| 0cc2249b97 | |||
| a5d0c0b6b7 | |||
| 3736a55bad | |||
| 303213da73 | |||
| 8d47eaf74c | |||
| 9f10c0dcf4 | |||
| d981e4c2c6 | |||
| 8f3d1bd091 | |||
| 3b9f557075 | |||
| 18cdec51d9 | |||
| 276a51b8c3 | |||
| 14e1d6aad3 | |||
| 9c65300fc4 | |||
| cf01dd9397 | |||
| 162318b439 | |||
| cd89cca984 | |||
| 25222c7310 | |||
| b2bfde4426 | |||
| 8602b53260 | |||
| 145b361581 | |||
| 7743dd8e7f | |||
| ed2886d449 | |||
| 002dfacc3e | |||
| fd9fbc32a8 | |||
| e54deeb4f7 | |||
| c85f211d5e | |||
| 67f848a9ee | |||
| fe336122e0 | |||
| 6d8d8493a6 | |||
| 5d7d249fff | |||
| 35ca030dfb | |||
| fab6fb0f5b | |||
| 3b8fc807f7 | |||
| 13cfb9ff14 | |||
| aabd5e75d8 | |||
| 7be6fb9501 | |||
| 9fc63e5cd3 | |||
| 7a1e8be48a | |||
| d299a65e03 | |||
| 2337f7a3ba | |||
| 4853d7e2fd | |||
| ca1a375f2d | |||
| 0d098815a2 | |||
| 30cbfb6ffd | |||
| 4c7deffdfe | |||
| 15133e561f | |||
| c235b52e3b | |||
| 903f356252 | |||
| 54c44b4379 | |||
| dcbeecc8cf | |||
| bf182c65ad | |||
| 7c5e4101b0 | |||
| 2736ac418f | |||
| e2dac10501 | |||
| c15d7ab28e | |||
| de78f8b1d5 | |||
| e56d32180c | |||
| 6c92837317 |
29
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"aard",
|
"aard",
|
||||||
|
"allowtransparency",
|
||||||
"ardetector",
|
"ardetector",
|
||||||
"autodetect",
|
"autodetect",
|
||||||
"autodetection",
|
"autodetection",
|
||||||
@ -8,11 +9,16 @@
|
|||||||
"blackbar",
|
"blackbar",
|
||||||
"blackbars",
|
"blackbars",
|
||||||
"blackframe",
|
"blackframe",
|
||||||
|
"bruteforce",
|
||||||
"canvas",
|
"canvas",
|
||||||
"clickthrough",
|
"clickthrough",
|
||||||
|
"cmpa",
|
||||||
|
"cmpb",
|
||||||
"com",
|
"com",
|
||||||
"comms",
|
"comms",
|
||||||
"csui",
|
"csui",
|
||||||
|
"ctab",
|
||||||
|
"cycleable",
|
||||||
"decycle",
|
"decycle",
|
||||||
"dinked",
|
"dinked",
|
||||||
"dinks",
|
"dinks",
|
||||||
@ -22,34 +28,49 @@
|
|||||||
"fith",
|
"fith",
|
||||||
"fitw",
|
"fitw",
|
||||||
"fuckup",
|
"fuckup",
|
||||||
|
"fucky",
|
||||||
|
"geoblocked",
|
||||||
|
"geoblocking",
|
||||||
"gfycat",
|
"gfycat",
|
||||||
"gmail",
|
"gmail",
|
||||||
"guardline",
|
"guardline",
|
||||||
"han",
|
"han",
|
||||||
"haram",
|
"haram",
|
||||||
|
"Heebo",
|
||||||
|
"hoverable",
|
||||||
"iframe",
|
"iframe",
|
||||||
"imgur",
|
"imgur",
|
||||||
"insta",
|
"insta",
|
||||||
|
"jsoneditor",
|
||||||
|
"jwplayer",
|
||||||
|
"kavin",
|
||||||
"letterboxed",
|
"letterboxed",
|
||||||
"manjaro",
|
"manjaro",
|
||||||
"mdicon",
|
"mdicon",
|
||||||
"mdijs",
|
"mdijs",
|
||||||
|
"metaivi",
|
||||||
"minification",
|
"minification",
|
||||||
"mitigations",
|
"mitigations",
|
||||||
|
"mstefan",
|
||||||
"nogrow",
|
"nogrow",
|
||||||
"noshrink",
|
"noshrink",
|
||||||
"outro",
|
"outro",
|
||||||
"PILLARBOX",
|
"PILLARBOX",
|
||||||
"PILLARBOXED",
|
"PILLARBOXED",
|
||||||
"polyfill",
|
"polyfill",
|
||||||
|
"problemo",
|
||||||
"recursing",
|
"recursing",
|
||||||
"reddit",
|
"reddit",
|
||||||
"rescan",
|
"rescan",
|
||||||
"resizer",
|
"resizer",
|
||||||
|
"resizers",
|
||||||
|
"SCPI",
|
||||||
"scrollbar",
|
"scrollbar",
|
||||||
"shitiness",
|
"shitiness",
|
||||||
"smallcaps",
|
"smallcaps",
|
||||||
|
"spaghettified",
|
||||||
"suboption",
|
"suboption",
|
||||||
|
"subscan",
|
||||||
"tabitem",
|
"tabitem",
|
||||||
"tablist",
|
"tablist",
|
||||||
"tamius",
|
"tamius",
|
||||||
@ -58,6 +79,7 @@
|
|||||||
"ultrawidify",
|
"ultrawidify",
|
||||||
"unmark",
|
"unmark",
|
||||||
"unmarking",
|
"unmarking",
|
||||||
|
"unsets",
|
||||||
"unshift",
|
"unshift",
|
||||||
"uwid",
|
"uwid",
|
||||||
"uwui",
|
"uwui",
|
||||||
@ -65,9 +87,14 @@
|
|||||||
"vids",
|
"vids",
|
||||||
"vuejs",
|
"vuejs",
|
||||||
"vuex",
|
"vuex",
|
||||||
|
"wakanim",
|
||||||
"webextension",
|
"webextension",
|
||||||
"webextensions",
|
"webextensions",
|
||||||
"youtube"
|
"wuckies",
|
||||||
|
"wucky",
|
||||||
|
"ycenter",
|
||||||
|
"youtube",
|
||||||
|
"YYMM"
|
||||||
],
|
],
|
||||||
"cSpell.ignoreWords": [
|
"cSpell.ignoreWords": [
|
||||||
"abcdefghijklmnopqrstuvwxyz",
|
"abcdefghijklmnopqrstuvwxyz",
|
||||||
|
|||||||
69
CHANGELOG.md
@ -1,18 +1,65 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Plans for the future
|
|
||||||
|
|
||||||
* Native builds for Chromium Edge
|
|
||||||
* Settings page looks ugly af right now. Maybe fix it some time later
|
|
||||||
* other bug fixes
|
|
||||||
|
|
||||||
## v7.0 (planned major)
|
|
||||||
* WebGL autodetection
|
|
||||||
|
|
||||||
## v6.0 (current major)
|
## v6.0 (current major)
|
||||||
|
|
||||||
|
### v6.4.0
|
||||||
|
* In-player UI now appears in full-screen even for websites that use top layer
|
||||||
|
* Embedded sites now inherit settings of the parent frame by default
|
||||||
|
* Setting inheritance/overriding is not thoroughly tested and may be full of edge cases.
|
||||||
|
* Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.
|
||||||
|
* Autodetection can now scan for subtitles.
|
||||||
|
* New experimental autodetection (which is secretly just slightly modified subtitle check)
|
||||||
|
|
||||||
|
### v6.3.0
|
||||||
|
* Added zoom segment to in-player UI and popup.
|
||||||
|
* Fixed keyboard zoom
|
||||||
|
* Added additional zoom options. If you wonder how zoom options differ from crop, mess around and find out.
|
||||||
|
* Subdomains now inherit same settings as their parent domain by default
|
||||||
|
* Extension popup detects embedded content
|
||||||
|
* Added `www.youtube-nocookie.com` to "officially supported" list
|
||||||
|
|
||||||
|
### v6.2.5
|
||||||
|
|
||||||
|
* Popup appearance changed — UI advertisement panel was moved to the popup header
|
||||||
|
* Fixed the bug where popup wouldn't be showing the correct settings
|
||||||
|
* Fixed the bug where site settings would default to 'disabled', even if Extension default setting was not disabled.
|
||||||
|
* Added ability to export and import settings from file (ft. developer mode editor)
|
||||||
|
* [dev goodies] Added debug overlay for aard
|
||||||
|
* Fixed an issue where aspect ratio wouldn't get calculated correctly on youtube videos with native aspect ratios other than 16:9
|
||||||
|
* Fixed an issue that would crash the extension if video element didn't have a player element associated with it
|
||||||
|
* Fixed an issue where extension sometimes wouldn't work if video element was grafted/re-parented to a different element
|
||||||
|
|
||||||
|
### v6.2.4
|
||||||
|
* [#264](https://github.com/tamius-han/ultrawidify/issues/264) — fixed issue with white screen that affected some youtube users. Special thanks to [SnowyOwlNugget](https://github.com/SnowyOwlNugget">SnowyOwlNugget), who instead of whining provided the necessary information.
|
||||||
|
* Minor updates to the settings
|
||||||
|
* Switching between full screen, theater, and normal player now correctly enables and disables the extension according to the settings.
|
||||||
|
* Don't reset settings if updating conf patches fails.
|
||||||
|
* Darken in-player UI backgrounds until Chrome fixes its backdrop-filter bug
|
||||||
|
|
||||||
|
### v6.2.3
|
||||||
|
* Fixed default persistent mode
|
||||||
|
|
||||||
|
### v6.2.2
|
||||||
|
* Fixed the issue where stretching was not applied if no cropping was used
|
||||||
|
* Added way to manually enable or disable color scheme detection, as a compromise between #259 and #264
|
||||||
|
|
||||||
|
### v6.2.1
|
||||||
|
|
||||||
|
* Fixed offset issue on Netflix
|
||||||
|
* Fixed issue with overlay iframe not being transparent on twitter
|
||||||
|
|
||||||
|
### v6.2.0
|
||||||
|
|
||||||
|
* Parts of automatic aspect ratio detection use WebGL now. This should result in improved performance.
|
||||||
|
* In-player UI now indicates whether a site is having a problem with automatic aspect ratio detection.
|
||||||
|
* UI improvements
|
||||||
|
|
||||||
|
Since automatic aspect ratio detection had to be rewritten completely and since the new version needs to be out 5 days ago, there are some regressions in the automatic aspect ratio detection.
|
||||||
|
|
||||||
|
### v6.1.0
|
||||||
|
|
||||||
|
Was skipped due to how major the changes were.
|
||||||
|
|
||||||
### v6.0.1
|
### v6.0.1
|
||||||
|
|
||||||
* Fixed external links
|
* Fixed external links
|
||||||
|
|||||||
39
CONTRIBUTING.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Contributing to Ultrawidify
|
||||||
|
|
||||||
|
Thank you for considering contributing to this project! We welcome contributions from the community and are grateful for your efforts.
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
0. Open an issue, where you introduce the feature you want to add or thing you want to fix
|
||||||
|
1. Fork the repository.
|
||||||
|
2. Create a new branch for your contribution.
|
||||||
|
3. Write readable code
|
||||||
|
4. Check that your additions do not break existing features
|
||||||
|
5. Open a pull request
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
Please follow the code of conduct:
|
||||||
|
|
||||||
|
* don't be too much of an asshole
|
||||||
|
* language policing my code comments is not a worthwhile contribution, and is considered spam. If you're here to do that, fuck off. I'm looking at you, ScamOSS and co.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- Use formatting consistent with the rest of the project
|
||||||
|
- Write meaningful commit messages.
|
||||||
|
- Include comments where necessary to explain complex logic.
|
||||||
|
|
||||||
|
## Licensing & Contribution Agreement
|
||||||
|
|
||||||
|
By submitting a contribution (e.g., via pull request), you agree to the following:
|
||||||
|
|
||||||
|
- You grant the project maintainer(s) a non-exclusive, irrevocable, worldwide, royalty-free license to use, copy, modify, and distribute your contributions as part of the project.
|
||||||
|
- You certify that your contributions are your original work and that you have the right to submit them.
|
||||||
|
- You agree that your contributions are licensed under the same license as the rest of the project, unless explicitly stated otherwise.
|
||||||
|
|
||||||
|
**TL;DR:** once your code is in the project, there's no take-backsies.
|
||||||
|
|
||||||
|
## Questions?
|
||||||
|
|
||||||
|
The discussion board is right over there, three tabs to the right.
|
||||||
35
LICENSE.MD
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2025 @tamius-han
|
||||||
|
|
||||||
|
The source code is provided on the "you can look, but you can't take" basis.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
You are permitted to:
|
||||||
|
|
||||||
|
* You can view the code
|
||||||
|
* You can create forks and modify the code for personal use
|
||||||
|
* You can build and run modified versions of this project for personal use on your local machine
|
||||||
|
* Push modifications to your personal github fork
|
||||||
|
|
||||||
|
## Restrictions
|
||||||
|
|
||||||
|
You may NOT:
|
||||||
|
|
||||||
|
* Distribute this project or any modified versions outside of your personal Github fork
|
||||||
|
* Publish or distribute compiled binaries, builds, or packages of this project or any derivative work
|
||||||
|
* Use the code in commercial products, services, or other public-facing deployments.
|
||||||
|
* Sub-license, sell, or otherwise make the software or derivatives available to third parties.
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
By submitting a pull request or other contribution, you agree that:
|
||||||
|
|
||||||
|
- You grant the project maintainer(s) a non-exclusive, irrevocable, worldwide, royalty-free license to use, copy, modify, and distribute your contributions as part of the project.
|
||||||
|
- You certify that your contributions are your original work and that you have the right to submit them.
|
||||||
|
- You agree that your contributions are licensed under the same license as the rest of the project, unless explicitly stated otherwise.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
This software is provided "as is", without warranty of any kind, express or implied. Use at your own risk.
|
||||||
33337
package-lock.json
generated
109
package.json
@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "ultrawidify",
|
"name": "ultrawidify",
|
||||||
"version": "6.0.1",
|
"version": "6.3.998",
|
||||||
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
||||||
"author": "Tamius Han <tamius.han@gmail.com>",
|
"author": "Tamius Han <tamius.han@gmail.com>",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run pre-build; cross-env NODE_ENV=production BROWSER=firefox CHANNEL=stable webpack --hide-modules",
|
"build": "npm run pre-build; cross-env NODE_ENV=production BROWSER=firefox CHANNEL=stable webpack",
|
||||||
"build-all": "bash ./scripts/build-all.sh",
|
"build-all": "bash ./scripts/build-all.sh",
|
||||||
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack --hide-modules",
|
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack",
|
||||||
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack --hide-modules",
|
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack",
|
||||||
"build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack --hide-modules",
|
"build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack",
|
||||||
"build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack --hide-modules",
|
"build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack",
|
||||||
"build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack --hide-modules",
|
"build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack",
|
||||||
"build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack --hide-modules",
|
"build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack",
|
||||||
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack --hide-modules",
|
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack",
|
||||||
"build-zip": "node scripts/build-zip.js",
|
"build-zip": "node scripts/build-zip.js",
|
||||||
"build:dev": "webpack --hide-modules",
|
"build:dev": "webpack",
|
||||||
"dev": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- --watch\" \"cross-env BROWSER=chrome npm run build:dev -- --watch\" \"cross-env BROWSER=edge npm run build:dev -- --watch\"",
|
"dev": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- --watch\" \"cross-env BROWSER=chrome npm run build:dev -- --watch\" \"cross-env BROWSER=edge npm run build:dev -- --watch\"",
|
||||||
"dev:windows": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- -w --watch-poll\" \"cross-env BROWSER=chrome npm run build:dev -- -w --watch-poll\" \"cross-env BROWSER=edge npm run build:dev -- -w --watch-poll\"",
|
"dev:windows": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- -w --watch-poll\" \"cross-env BROWSER=chrome npm run build:dev -- -w --watch-poll\" \"cross-env BROWSER=edge npm run build:dev -- -w --watch-poll\"",
|
||||||
"dev-ff": "rm -rf ./dist-ff && cross-env NODE_ENV=development CHANNEL=dev BROWSER=firefox npm run build:dev -- --watch",
|
"dev-ff": "rm -rf ./dist-ff && cross-env NODE_ENV=development CHANNEL=dev BROWSER=firefox npm run build:dev -- --watch",
|
||||||
@ -25,55 +25,64 @@
|
|||||||
"start:windows": "npm run dev:windows"
|
"start:windows": "npm run dev:windows"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@mdi/font": "^6.5.95",
|
"@mdi/font": "^7.4.47",
|
||||||
"@mdi/js": "^6.4.95",
|
"@mdi/js": "^7.4.47",
|
||||||
"@types/resize-observer-browser": "^0.1.6",
|
"@types/resize-observer-browser": "^0.1.11",
|
||||||
"concurrently": "^5.3.0",
|
"concurrently": "^9.2.1",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^11.3.2",
|
||||||
"json-cyclic": "0.0.3",
|
"gl-matrix": "^3.4.4",
|
||||||
|
"json-cyclic": "1.0.2",
|
||||||
|
"json-difference": "^1.16.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mdi-vue": "^3.0.11",
|
"mdi-vue": "^3.0.13",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^5.9.3",
|
||||||
"vue": "^3.2.21",
|
"vanilla-jsoneditor": "^3.10.0",
|
||||||
|
"vue": "^3.5.25",
|
||||||
"vue-style-loader": "^4.1.3",
|
"vue-style-loader": "^4.1.3",
|
||||||
"vuex": "^4.0.2",
|
"vuex": "^4.1.0",
|
||||||
"vuex-webextensions": "^1.3.3",
|
"vuex-webextensions": "^1.3.3",
|
||||||
"webextension-polyfill": "^0.12.0"
|
"webextension-polyfill": "^0.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@babel/core": "^7.28.5",
|
||||||
"@babel/preset-env": "^7.16.0",
|
"@babel/preset-env": "^7.28.5",
|
||||||
"@types/chrome": "0.0.240",
|
"@tailwindcss/postcss": "^4.1.17",
|
||||||
"@types/core-js": "^2.5.5",
|
"@types/chrome": "0.1.31",
|
||||||
"@types/es6-promise": "^3.3.0",
|
"@types/core-js": "^2.5.8",
|
||||||
"@types/firefox": "0.0.31",
|
"@types/es6-promise": "^3.3.2",
|
||||||
"@types/lodash": "^4.14.176",
|
"@types/firefox": "0.0.34",
|
||||||
"@types/node": "^14.17.32",
|
"@types/lodash": "^4.17.21",
|
||||||
"@vue/cli": "^4.5.15",
|
"@types/node": "^24.10.1",
|
||||||
"@vue/cli-plugin-typescript": "^4.5.15",
|
"@vue/cli": "^5.0.9",
|
||||||
"@vue/compiler-sfc": "^3.2.21",
|
"@vue/cli-plugin-typescript": "^5.0.9",
|
||||||
"archiver": "^3.0.0",
|
"@vue/compiler-sfc": "^3.5.25",
|
||||||
"babel-loader": "^8.2.3",
|
"archiver": "^7.0.1",
|
||||||
|
"babel-loader": "^10.0.0",
|
||||||
"babel-preset-es2020": "^1.0.2",
|
"babel-preset-es2020": "^1.0.2",
|
||||||
"copy-webpack-plugin": "^4.5.3",
|
"copy-webpack-plugin": "^13.0.1",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^10.1.0",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^7.1.2",
|
||||||
"ejs": "^2.7.4",
|
"ejs": "^3.1.10",
|
||||||
"file-loader": "^1.1.11",
|
"file-loader": "^6.2.0",
|
||||||
"mini-css-extract-plugin": "^0.4.4",
|
"mini-css-extract-plugin": "^2.9.4",
|
||||||
"node-sass": "^4.14.1",
|
"postcss": "^8.5.6",
|
||||||
|
"postcss-import": "^16.1.1",
|
||||||
|
"postcss-nested": "^7.0.2",
|
||||||
|
"postcss-preset-env": "^10.4.0",
|
||||||
|
"postcss-strip-inline-comments": "^0.1.5",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"resolve-url-loader": "^5.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"sass-loader": "^7.1.0",
|
"tailwindcss": "^4.1.17",
|
||||||
"ts-loader": "^8.3.0",
|
"ts-loader": "^9.5.4",
|
||||||
|
"tsconfig-paths": "^4.2.0",
|
||||||
"vue-cli-plugin-vue-next": "~0.1.4",
|
"vue-cli-plugin-vue-next": "~0.1.4",
|
||||||
"vue-loader": "^16.8.2",
|
"vue-loader": "^17.4.2",
|
||||||
"web-ext-types": "^2.3.0",
|
"vue-tsc": "^3.1.5",
|
||||||
"webpack": "^4.46.0",
|
"web-ext-types": "^3.2.1",
|
||||||
"webpack-chrome-extension-reloader": "^0.8.3",
|
"webpack": "^5.103.0",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^6.0.1",
|
||||||
"webpack-shell-plugin": "^0.5.0",
|
"webpack-shell-plugin": "^0.5.0",
|
||||||
"@vue/cli": "^4.5.15",
|
"webpack-shell-plugin-next": "^2.3.3"
|
||||||
"@vue/cli-plugin-typescript": "^4.5.15"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
paths.config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'@src': path.resolve(__dirname, 'src'),
|
||||||
|
'@ui': path.resolve(__dirname, 'src/ui'),
|
||||||
|
'@components': path.resolve(__dirname, 'src/ui/components'),
|
||||||
|
'@res': path.resolve(__dirname, 'src/ui/res'),
|
||||||
|
'@': path.resolve(__dirname, 'src'),
|
||||||
|
};
|
||||||
|
|
||||||
24
postcss.config.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const aliases = require('./paths.config');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('postcss-import')({
|
||||||
|
resolve(id) {
|
||||||
|
for (const alias in aliases) {
|
||||||
|
if (id === alias || id.startsWith(alias + '/')) {
|
||||||
|
return path.resolve(
|
||||||
|
aliases[alias],
|
||||||
|
id.slice(alias.length + 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
require('@tailwindcss/postcss'),
|
||||||
|
require('postcss-nested'),
|
||||||
|
require('autoprefixer'),
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -5,11 +5,27 @@
|
|||||||
# any other way probably isn't going to work.
|
# any other way probably isn't going to work.
|
||||||
|
|
||||||
# pre-build steps:
|
# pre-build steps:
|
||||||
|
|
||||||
|
echo "—————————————————————————————————————————————————————";
|
||||||
|
echo " BUILDING ALL ULTRAWIDIFY VERSIONS";
|
||||||
|
echo "—————————————————————————————————————————————————————";
|
||||||
|
|
||||||
mkdir -p ./build/old
|
mkdir -p ./build/old
|
||||||
npm run pre-build
|
npm run pre-build
|
||||||
|
|
||||||
|
echo "::"
|
||||||
|
echo "::"
|
||||||
|
echo "::"
|
||||||
|
echo ":: Pre-build complete. Moving old builds from dist-zip ..."
|
||||||
|
|
||||||
rm ./dist-zip/uw-amo-source.zip
|
rm ./dist-zip/uw-amo-source.zip
|
||||||
mv -f ./dist-zip/*.zip ./build/old
|
mv -f ./dist-zip/*.zip ./build/old
|
||||||
|
|
||||||
|
echo "::"
|
||||||
|
echo "::"
|
||||||
|
echo "::"
|
||||||
|
echo ":: dist-zip is clean. Starting builds ..."
|
||||||
|
|
||||||
# lets force raise ram limit, but the improper way
|
# lets force raise ram limit, but the improper way
|
||||||
# export NODE_OPTIONS=--max_old_space_size=4096
|
# export NODE_OPTIONS=--max_old_space_size=4096
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ const fs = require('fs');
|
|||||||
|
|
||||||
const BUNDLE_DIR = path.join(__dirname, `../dist-${process.env.BROWSER === 'firefox' ? 'ff' : process.env.BROWSER}`);
|
const BUNDLE_DIR = path.join(__dirname, `../dist-${process.env.BROWSER === 'firefox' ? 'ff' : process.env.BROWSER}`);
|
||||||
const bundles = [
|
const bundles = [
|
||||||
'csui/csui-popup.js',
|
// 'csui/csui-popup.js',
|
||||||
];
|
];
|
||||||
|
|
||||||
const evalRegexForProduction = /;([a-z])=function\(\){return this}\(\);try{\1=\1\|\|Function\("return this"\)\(\)\|\|\(0,eval\)\("this"\)}catch\(t\){"object"==typeof window&&\(\1=window\)}/g;
|
const evalRegexForProduction = /;([a-z])=function\(\){return this}\(\);try{\1=\1\|\|Function\("return this"\)\(\)\|\|\(0,eval\)\("this"\)}catch\(t\){"object"==typeof window&&\(\1=window\)}/g;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
enum AspectRatioType {
|
enum AspectRatioType {
|
||||||
|
Default = -3, // inherit from global settings
|
||||||
Cycle = -2,
|
Cycle = -2,
|
||||||
Initial = -1, // page default
|
Initial = -1, // page default
|
||||||
Reset = 0, // reset to initial
|
Reset = 0, // reset to initial
|
||||||
@ -8,6 +9,7 @@ enum AspectRatioType {
|
|||||||
Fixed = 4, // pre-determined aspect ratio
|
Fixed = 4, // pre-determined aspect ratio
|
||||||
Manual = 5, // ratio achieved by zooming in/zooming out
|
Manual = 5, // ratio achieved by zooming in/zooming out
|
||||||
AutomaticUpdate = 6, // set by Aard
|
AutomaticUpdate = 6, // set by Aard
|
||||||
|
Cover = 7, // replaces FitWidth and FitHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AspectRatioType;
|
export default AspectRatioType;
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
enum EmbeddedContentSettingsOverridePolicy {
|
||||||
|
Always = 2,
|
||||||
|
UseAsDefault = 1,
|
||||||
|
Default = 0,
|
||||||
|
Never = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EmbeddedContentSettingsOverridePolicy;
|
||||||
@ -1,10 +1,9 @@
|
|||||||
enum ExtensionMode {
|
enum ExtensionMode {
|
||||||
AutoDisabled = -2,
|
|
||||||
Disabled = -1,
|
Disabled = -1,
|
||||||
Default = 0,
|
Default = 0,
|
||||||
Whitelist = 1,
|
FullScreen = 1,
|
||||||
Basic = 2,
|
Theater = 2,
|
||||||
Enabled = 3,
|
All = 3
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ExtensionMode;
|
export default ExtensionMode;
|
||||||
|
|||||||
5
src/common/enums/InputHandlingMode.enum.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum InputHandlingMode {
|
||||||
|
Disabled = -1,
|
||||||
|
Default = 0,
|
||||||
|
Enabled = 1, // enable, but don't override site shortcuts
|
||||||
|
}
|
||||||
10
src/common/enums/LegacyExtensionMode.enum.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
enum LegacyExtensionMode {
|
||||||
|
AutoDisabled = -2,
|
||||||
|
Disabled = -1,
|
||||||
|
Default = 0,
|
||||||
|
Whitelist = 1,
|
||||||
|
Basic = 2,
|
||||||
|
Enabled = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LegacyExtensionMode;
|
||||||
5
src/common/enums/PlayerDetectionMode.enum.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum PlayerDetectionMode {
|
||||||
|
Auto = 0,
|
||||||
|
AncestorIndex = 1,
|
||||||
|
QuerySelectors = 2,
|
||||||
|
};
|
||||||
9
src/common/enums/SiteSupportLevel.enum.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export enum SiteSupportLevel {
|
||||||
|
OfficialSupport = 'official',
|
||||||
|
CommunitySupport = 'community',
|
||||||
|
UserDefined = 'user-defined',
|
||||||
|
BetaSupport = 'testing',
|
||||||
|
OfficialBlacklist = 'officially-disabled',
|
||||||
|
Unknown = 'unknown',
|
||||||
|
UserModified = 'modified',
|
||||||
|
}
|
||||||
@ -4,7 +4,8 @@ enum VideoAlignmentType {
|
|||||||
Right = 2,
|
Right = 2,
|
||||||
Top = 3,
|
Top = 3,
|
||||||
Bottom = 4,
|
Bottom = 4,
|
||||||
Default = -1
|
Default = -1,
|
||||||
|
Custom = -2,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default VideoAlignmentType;
|
export default VideoAlignmentType;
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
import AspectRatioType from '../enums/AspectRatioType.enum';
|
import AspectRatioType from '../enums/AspectRatioType.enum';
|
||||||
|
|
||||||
|
export enum ArVariant {
|
||||||
|
Crop = undefined,
|
||||||
|
Zoom = 1
|
||||||
|
}
|
||||||
|
|
||||||
export interface Ar {
|
export interface Ar {
|
||||||
type: AspectRatioType,
|
type: AspectRatioType,
|
||||||
ratio?: number
|
ratio?: number,
|
||||||
|
variant?: ArVariant,
|
||||||
|
offset?: number,
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/common/interfaces/ClientUiMenu.interface.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { CommandInterface, InPlayerUIOptions } from '@src/common/interfaces/SettingsInterface';
|
||||||
|
|
||||||
|
export interface MenuItemBaseConfig {
|
||||||
|
label?: string;
|
||||||
|
subitems?: MenuItemConfig[];
|
||||||
|
command?: CommandInterface,
|
||||||
|
action?: () => void;
|
||||||
|
customHTML?: HTMLElement | string;
|
||||||
|
customId?: string;
|
||||||
|
customClassList?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MenuItemConfig = MenuItemBaseConfig & ({label: string} | {customHTML: HTMLElement | string});
|
||||||
|
|
||||||
|
export enum MenuPosition {
|
||||||
|
TopLeft = 'top-left',
|
||||||
|
Left = 'left',
|
||||||
|
BottomLeft = 'bottom-left',
|
||||||
|
Top = 'top',
|
||||||
|
Bottom = 'bottom',
|
||||||
|
TopRight = 'top-right',
|
||||||
|
Right = 'right',
|
||||||
|
BottomRight = 'bottom-right',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MenuConfig {
|
||||||
|
isGlobal?: boolean;
|
||||||
|
ui: InPlayerUIOptions;
|
||||||
|
options?: {forceShow?: boolean};
|
||||||
|
items: MenuItemConfig[];
|
||||||
|
}
|
||||||
43
src/common/interfaces/EventBusMessage.interface.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { CommsOrigin } from '@/ext/module/comms/CommsClient';
|
||||||
|
import type { Runtime } from 'chrome';
|
||||||
|
|
||||||
|
export interface EventBusCommand {
|
||||||
|
isGlobal?: boolean,
|
||||||
|
source?: any,
|
||||||
|
function: (commandData: any, context?: EventBusContext) => void | Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventBusMessage<T = unknown> {
|
||||||
|
command: string;
|
||||||
|
config?: T;
|
||||||
|
context?: EventBusContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventBusContext {
|
||||||
|
stopPropagation?: boolean;
|
||||||
|
|
||||||
|
origin?: CommsOrigin;
|
||||||
|
frameUrl?: string;
|
||||||
|
|
||||||
|
// tab?: number;
|
||||||
|
// frame?: number;
|
||||||
|
// port?: string;
|
||||||
|
|
||||||
|
visitedBusses?: string[];
|
||||||
|
commandId?: string;
|
||||||
|
|
||||||
|
comms?: {
|
||||||
|
forwardTo?: 'all' | 'active' | 'popup' | 'contentScript' | 'all-frames';
|
||||||
|
sender?: Runtime.MessageSender;
|
||||||
|
port?: Runtime.Port;
|
||||||
|
// frame?: any;
|
||||||
|
sourceFrame?: {
|
||||||
|
tabId: number;
|
||||||
|
frameId: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
borderCrossings?: {
|
||||||
|
commsServer?: boolean,
|
||||||
|
iframe?: boolean,
|
||||||
|
};
|
||||||
|
}
|
||||||
8
src/common/interfaces/HostData.interface.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
|
||||||
|
|
||||||
|
export interface HostInfo {
|
||||||
|
host: string,
|
||||||
|
hasVideo: boolean,
|
||||||
|
minEnvironment: ExtensionEnvironment,
|
||||||
|
maxEnvironment: ExtensionEnvironment
|
||||||
|
};
|
||||||
6
src/common/interfaces/IframeTunnelPayload.interface.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { EventBusMessage } from '@src/common/interfaces/EventBusMessage.interface';
|
||||||
|
|
||||||
|
export interface IframeTunnelPayload {
|
||||||
|
action: string,
|
||||||
|
payload: EventBusMessage,
|
||||||
|
}
|
||||||
19
src/common/interfaces/ScalingParamsBroadcast.interface.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum';
|
||||||
|
import { Ar } from '@src/common/interfaces/ArInterface';
|
||||||
|
import { Stretch } from '@src/common/interfaces/StretchInterface';
|
||||||
|
|
||||||
|
export interface ScalingParamsBroadcast {
|
||||||
|
effectiveZoom: {
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
},
|
||||||
|
videoAlignment: {
|
||||||
|
x: VideoAlignmentType,
|
||||||
|
y: VideoAlignmentType,
|
||||||
|
xPos?: number,
|
||||||
|
yPos?: number,
|
||||||
|
}
|
||||||
|
lastAr: Ar,
|
||||||
|
manualZoom: boolean,
|
||||||
|
stretch: Stretch,
|
||||||
|
}
|
||||||
@ -1,10 +1,28 @@
|
|||||||
import { Action } from '../../../node_modules/vuex/types/index'
|
import { AardPollingOptions } from '@src/ext/module/aard/enums/aard-polling-options.enum'
|
||||||
import AntiGradientMode from '../enums/AntiGradientMode.enum'
|
import { AardSubtitleCropMode } from '@src/ext/module/aard/enums/aard-subtitle-crop-mode.enum'
|
||||||
import AspectRatioType from '../enums/AspectRatioType.enum'
|
import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
|
||||||
import CropModePersistence from '../enums/CropModePersistence.enum'
|
import AspectRatioType from '@src/common/enums/AspectRatioType.enum'
|
||||||
import ExtensionMode from '../enums/ExtensionMode.enum'
|
import CropModePersistence from '@src/common/enums/CropModePersistence.enum'
|
||||||
import StretchType from '../enums/StretchType.enum'
|
import EmbeddedContentSettingsOverridePolicy from '@src/common/enums/EmbeddedContentSettingsOverridePolicy.enum'
|
||||||
import VideoAlignmentType from '../enums/VideoAlignmentType.enum'
|
import ExtensionMode from '@src/common/enums/ExtensionMode.enum'
|
||||||
|
import StretchType from '@src/common/enums/StretchType.enum'
|
||||||
|
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum'
|
||||||
|
import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
|
||||||
|
import { InputHandlingMode } from '@src/common/enums/InputHandlingMode.enum';
|
||||||
|
import { MenuPosition } from '@src/common/interfaces/ClientUiMenu.interface';
|
||||||
|
|
||||||
|
export enum ExtensionEnvironment {
|
||||||
|
Normal = ExtensionMode.All,
|
||||||
|
Theater = ExtensionMode.Theater,
|
||||||
|
Fullscreen = ExtensionMode.FullScreen,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DevUiConfig {
|
||||||
|
aardDebugOverlay: {
|
||||||
|
showOnStartup: boolean,
|
||||||
|
showDetectionDetails: boolean,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface KeyboardShortcutInterface {
|
export interface KeyboardShortcutInterface {
|
||||||
key?: string,
|
key?: string,
|
||||||
@ -32,12 +50,6 @@ interface RestrictionsSettings {
|
|||||||
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
|
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExtensionEnvironmentSettingsInterface {
|
|
||||||
normal: ExtensionMode,
|
|
||||||
theater: ExtensionMode,
|
|
||||||
fullscreen: ExtensionMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommandInterface {
|
export interface CommandInterface {
|
||||||
action: string,
|
action: string,
|
||||||
label: string,
|
label: string,
|
||||||
@ -51,139 +63,285 @@ export interface CommandInterface {
|
|||||||
export type SettingsReloadComponent = 'PlayerData' | 'VideoData';
|
export type SettingsReloadComponent = 'PlayerData' | 'VideoData';
|
||||||
export type SettingsReloadFlags = true | SettingsReloadComponent;
|
export type SettingsReloadFlags = true | SettingsReloadComponent;
|
||||||
|
|
||||||
|
export interface AardSubtitleScanOptions {
|
||||||
|
subtitleCropMode: AardSubtitleCropMode,
|
||||||
|
resumeAfter: number, // resume after X ms of no subtitle
|
||||||
|
scanSpacing: number, // repeat subtitle scan every _n_ lines
|
||||||
|
scanMargin: number, // % of pixels (from edge to start area) that we skip while scanning.
|
||||||
|
// While technically anything between 0 and 0.5 is valid, the value
|
||||||
|
// should be somewhere between 0.1-0.3 in order for subtitle scan to
|
||||||
|
// work properly.
|
||||||
|
maxValidLetter: number, // if letter is longer than this, something's off.
|
||||||
|
subtitleSubpixelThresholdOn: number,
|
||||||
|
subtitleSubpixelThresholdOff: number,
|
||||||
|
|
||||||
|
minDetections: number,
|
||||||
|
minImageLineDetections: number,
|
||||||
|
|
||||||
|
maxPotentialSubtitleMisalignment: number, // how many pixels off-center can "potential subtitle" be
|
||||||
|
|
||||||
|
refiningScanSpacing: number, // must be base-2
|
||||||
|
refiningScanInitialIterations: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AardSettings {
|
||||||
|
aardType: 'webgl' | 'legacy' | 'auto';
|
||||||
|
useLegacy: boolean,
|
||||||
|
|
||||||
|
earlyStopOptions: {
|
||||||
|
stopAfterFirstDetection: boolean;
|
||||||
|
stopAfterTimeout: boolean;
|
||||||
|
stopTimeout: number;
|
||||||
|
},
|
||||||
|
|
||||||
|
polling: {
|
||||||
|
runInBackgroundTabs: AardPollingOptions;
|
||||||
|
runOnSmallVideos: AardPollingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
|
||||||
|
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
|
||||||
|
// Any more and we don't adjust ar.
|
||||||
|
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
|
||||||
|
timers: { // autodetection frequency
|
||||||
|
playing: number, // while playing
|
||||||
|
playingReduced: number, // while video/player element has insufficient size
|
||||||
|
paused: number, // while paused
|
||||||
|
error: number, // after error
|
||||||
|
minimumTimeout: number,
|
||||||
|
tickrate: number, // 1 tick every this many milliseconds
|
||||||
|
},
|
||||||
|
|
||||||
|
subtitles: AardSubtitleScanOptions,
|
||||||
|
|
||||||
|
autoDisable: { // settings for automatically disabling the extension
|
||||||
|
onFirstChange: boolean,
|
||||||
|
ifNotChanged: boolean,
|
||||||
|
ifNotChangedTimeout: number,
|
||||||
|
ifSubtitles: boolean;
|
||||||
|
},
|
||||||
|
canvasDimensions: {
|
||||||
|
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
|
||||||
|
// it's not used to detect aspect ratio by itself, so it can be tiny af
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
|
||||||
|
// at the expense of performance
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
blackLevels: {
|
||||||
|
defaultBlack: number, // By default, pixels darker than this are considered black.
|
||||||
|
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
|
||||||
|
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
|
||||||
|
// It is not considered a valid image detection if gradient detection is enabled
|
||||||
|
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
|
||||||
|
}
|
||||||
|
sampling: {
|
||||||
|
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
|
||||||
|
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
|
||||||
|
randomCols: number, // we add this many randomly selected columns to the static columns
|
||||||
|
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
|
||||||
|
},
|
||||||
|
|
||||||
|
// pls deprecate and move things used
|
||||||
|
edgeDetection: {
|
||||||
|
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
|
||||||
|
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient
|
||||||
|
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient
|
||||||
|
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient
|
||||||
|
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
|
||||||
|
|
||||||
|
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
|
||||||
|
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable
|
||||||
|
minEdgeSegmentSize: number,
|
||||||
|
averageEdgeThreshold: number, // average edge must be this many px
|
||||||
|
},
|
||||||
|
|
||||||
|
letterboxOrientationScan: {
|
||||||
|
letterboxLimit: number, // how many non-black pixels we can detect before ruling out letterbox
|
||||||
|
pillarboxLimit: number, // how many non-black pixels we can detect before ruling out pillarbox
|
||||||
|
}
|
||||||
|
|
||||||
|
pillarTest: {
|
||||||
|
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
|
||||||
|
allowMisaligned: number // left and right edge can vary this much (%)
|
||||||
|
},
|
||||||
|
textLineTest: {
|
||||||
|
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
|
||||||
|
// with text. This value is relative to canvas width (%)
|
||||||
|
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
|
||||||
|
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
|
||||||
|
// is over 50% of the canvas width
|
||||||
|
testRowOffset: number // we test this % of height from detected edge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AardLegacySettings {
|
||||||
|
aardType: 'webgl' | 'legacy' | 'auto';
|
||||||
|
|
||||||
|
earlyStopOptions: {
|
||||||
|
stopAfterFirstDetection: boolean;
|
||||||
|
stopAfterTimeout: boolean;
|
||||||
|
stopTimeout: number;
|
||||||
|
},
|
||||||
|
|
||||||
|
polling: {
|
||||||
|
runInBackgroundTabs: AardPollingOptions;
|
||||||
|
runOnSmallVideos: AardPollingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
|
||||||
|
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
|
||||||
|
// Any more and we don't adjust ar.
|
||||||
|
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
|
||||||
|
timers: { // autodetection frequency
|
||||||
|
playing: number, // while playing
|
||||||
|
playingReduced: number, // while video/player element has insufficient size
|
||||||
|
paused: number, // while paused
|
||||||
|
error: number, // after error
|
||||||
|
minimumTimeout: number,
|
||||||
|
tickrate: number, // 1 tick every this many milliseconds
|
||||||
|
},
|
||||||
|
|
||||||
|
subtitles: AardSubtitleScanOptions,
|
||||||
|
|
||||||
|
autoDisable: { // settings for automatically disabling the extension
|
||||||
|
onFirstChange: boolean,
|
||||||
|
ifNotChanged: boolean,
|
||||||
|
ifNotChangedTimeout: number,
|
||||||
|
ifSubtitles: boolean;
|
||||||
|
},
|
||||||
|
canvasDimensions: {
|
||||||
|
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
|
||||||
|
// it's not used to detect aspect ratio by itself, so it can be tiny af
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
|
||||||
|
// at the expense of performance
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
blackLevels: {
|
||||||
|
defaultBlack: number, // By default, pixels darker than this are considered black.
|
||||||
|
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
|
||||||
|
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
|
||||||
|
// It is not considered a valid image detection if gradient detection is enabled
|
||||||
|
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
|
||||||
|
}
|
||||||
|
sampling: {
|
||||||
|
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
|
||||||
|
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
|
||||||
|
randomCols: number, // we add this many randomly selected columns to the static columns
|
||||||
|
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
|
||||||
|
},
|
||||||
|
|
||||||
|
// pls deprecate and move things used
|
||||||
|
edgeDetection: {
|
||||||
|
slopeTestWidth: number,
|
||||||
|
gradientTestSamples: number, // we check this many pixels below (or above) the suspected edge to check for gradient
|
||||||
|
gradientTestBlackThreshold: number, // if pixel in test sample is brighter than that, we aren't looking at gradient
|
||||||
|
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
|
||||||
|
|
||||||
|
thresholds: {
|
||||||
|
edgeDetectionLimit: number, // during scanning of the edge, quit after edge gets detected at this many points
|
||||||
|
minQualitySingleEdge: number, // At least one of the detected must reach this quality
|
||||||
|
minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
|
||||||
|
}
|
||||||
|
|
||||||
|
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
|
||||||
|
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient
|
||||||
|
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient
|
||||||
|
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient
|
||||||
|
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
|
||||||
|
|
||||||
|
// Previous iteration variables VVVV
|
||||||
|
sampleWidth: number, // we take a sample this wide for edge detection
|
||||||
|
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
|
||||||
|
confirmationThreshold: number, //
|
||||||
|
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
|
||||||
|
// to confirm an edge in case there's no edges on top or bottom (other
|
||||||
|
// than logo, of course)
|
||||||
|
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
|
||||||
|
// or watermark.
|
||||||
|
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
|
||||||
|
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
|
||||||
|
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
|
||||||
|
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
|
||||||
|
// continue with search. It's pointless, because black edge is higher/lower than we
|
||||||
|
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
|
||||||
|
edgeMismatchTolerancePx: number,// corners and center are considered equal if they differ by at most this many px
|
||||||
|
|
||||||
|
|
||||||
|
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
|
||||||
|
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable
|
||||||
|
minEdgeSegmentSize: number,
|
||||||
|
averageEdgeThreshold: number, // average edge must be this many px
|
||||||
|
},
|
||||||
|
|
||||||
|
letterboxOrientationScan: {
|
||||||
|
letterboxLimit: number, // how many non-black pixels we can detect before ruling out letterbox
|
||||||
|
pillarboxLimit: number, // how many non-black pixels we can detect before ruling out pillarbox
|
||||||
|
}
|
||||||
|
|
||||||
|
pillarTest: {
|
||||||
|
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
|
||||||
|
allowMisaligned: number // left and right edge can vary this much (%)
|
||||||
|
},
|
||||||
|
textLineTest: {
|
||||||
|
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
|
||||||
|
// with text. This value is relative to canvas width (%)
|
||||||
|
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
|
||||||
|
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
|
||||||
|
// is over 50% of the canvas width
|
||||||
|
testRowOffset: number // we test this % of height from detected edge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface DevSettings {
|
||||||
|
loadFromSnapshot: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InPlayerUIOptions {
|
||||||
|
activatorAlignment: MenuPosition,
|
||||||
|
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
|
||||||
|
minEnabledHeight: number, // don't show UI if player is narrower than % of screen height
|
||||||
|
activation: 'player' | 'trigger-zone' | 'distance' | 'none', // what needs to be hovered in order for UI to be visible
|
||||||
|
activateWithCtrl: boolean,
|
||||||
|
activationDistance: number,
|
||||||
|
activationDistanceUnits: '%' | 'px',
|
||||||
|
activatorPadding: {x: number, y: number}
|
||||||
|
activatorPaddingUnit: {x: '%' | 'px', y: '%' | 'px'},
|
||||||
|
triggerZoneDimensions: { // how large the trigger zone is (relative to player size)
|
||||||
|
width: number
|
||||||
|
height: number,
|
||||||
|
offsetX: number, // fed to translateX(offsetX + '%'). Valid range [-100, 0]
|
||||||
|
offsetY: number // fed to translateY(offsetY + '%'). Valid range [-100, 100]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
interface SettingsInterface {
|
interface SettingsInterface {
|
||||||
_updateFlags?: {
|
_updateFlags?: {
|
||||||
requireReload?: SettingsReloadFlags,
|
requireReload?: SettingsReloadFlags,
|
||||||
forSite?: string
|
forSite?: string
|
||||||
}
|
}
|
||||||
|
dev: DevSettings,
|
||||||
|
|
||||||
arDetect: {
|
aardLegacy: AardLegacySettings,
|
||||||
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
|
aard: AardSettings,
|
||||||
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
|
|
||||||
// Any more and we don't adjust ar.
|
|
||||||
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
|
|
||||||
timers: { // autodetection frequency
|
|
||||||
playing: number, // while playing
|
|
||||||
paused: number, // while paused
|
|
||||||
error: number, // after error
|
|
||||||
minimumTimeout: number,
|
|
||||||
tickrate: number, // 1 tick every this many milliseconds
|
|
||||||
},
|
|
||||||
autoDisable: { // settings for automatically disabling the extension
|
|
||||||
maxExecutionTime: number, // if execution time of main autodetect loop exceeds this many milliseconds,
|
|
||||||
// we disable it.
|
|
||||||
consecutiveTimeoutCount: number, // we only do it if it happens this many consecutive times
|
|
||||||
|
|
||||||
// FOR FUTURE USE
|
ui: {
|
||||||
consecutiveArResets: number // if aspect ratio reverts immediately after AR change is applied, we disable everything
|
inPlayer: InPlayerUIOptions,
|
||||||
},
|
devMode?: boolean,
|
||||||
canvasDimensions: {
|
dev: DevUiConfig,
|
||||||
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
|
}
|
||||||
// it's not used to detect aspect ratio by itself, so it can be tiny af
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
},
|
|
||||||
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
|
|
||||||
// at the expense of performance
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// samplingInterval: 10, // we sample at columns at (width/this) * [ 1 .. this - 1]
|
|
||||||
blackframe: {
|
|
||||||
sufficientColorVariance: number, // calculate difference between average intensity and pixel, for every pixel for every color
|
|
||||||
// component. Average intensity is normalized to where 0 is black and 1 is biggest value for
|
|
||||||
// that component. If sum of differences between normalized average intensity and normalized
|
|
||||||
// component varies more than this % between color components, we can afford to use less strict
|
|
||||||
// cumulative threshold.
|
|
||||||
cumulativeThresholdLax: number,
|
|
||||||
cumulativeThresholdStrict: number,// if we add values of all pixels together and get more than this, the frame is bright enough.
|
|
||||||
// (note: blackframe is 16x9 px -> 144px total. cumulative threshold can be reached fast)
|
|
||||||
blackPixelsCondition: number, // How much pixels must be black (1 all, 0 none) before we consider frame as black. Takes
|
|
||||||
// precedence over cumulative threshold: if blackPixelsCondition is met, the frame is dark
|
|
||||||
// regardless of whether cumulative threshold has been reached.
|
|
||||||
},
|
|
||||||
blackbar: {
|
|
||||||
blackLevel: number, // everything darker than 10/255 across all RGB components is considered black by
|
|
||||||
// default. blackLevel can decrease if we detect darker black.
|
|
||||||
threshold: number, // if pixel is darker than the sum of black level and this value, we count it as black
|
|
||||||
// on 0-255. Needs to be fairly high (8 might not cut it) due to compression
|
|
||||||
// artifacts in the video itself
|
|
||||||
frameThreshold: number, // threshold, but when doing blackframe test
|
|
||||||
imageThreshold: number, // in order to detect pixel as "not black", the pixel must be brighter than
|
|
||||||
// the sum of black level, threshold and this value.
|
|
||||||
gradientThreshold: number, // When trying to determine thickness of the black bars, we take 2 values: position of
|
|
||||||
// the last pixel that's darker than our threshold, and position of the first pixel that's
|
|
||||||
// brighter than our image threshold. If positions are more than this many pixels apart,
|
|
||||||
// we assume we aren't looking at letterbox and thus don't correct the aspect ratio.
|
|
||||||
gradientSampleSize: number, // How far do we look to find the gradient
|
|
||||||
maxGradient: number, // if two neighboring pixels in gradientSampleSize differ by more than this, then we aren't
|
|
||||||
// looking at a gradient
|
|
||||||
gradientNegativeTreshold: number,
|
|
||||||
gradientMaxSD: number, // reserved for future use
|
|
||||||
antiGradientMode: AntiGradientMode
|
|
||||||
},
|
|
||||||
variableBlackbarThresholdOptions: { // In case of poor bitrate videos, jpeg artifacts may cause us issues
|
|
||||||
// FOR FUTURE USE
|
|
||||||
enabled: boolean, // allow increasing blackbar threshold
|
|
||||||
disableArDetectOnMax: boolean, // disable autodetection when threshold goes over max blackbar threshold
|
|
||||||
maxBlackbarThreshold: number, // max threshold (don't increase past this)
|
|
||||||
thresholdStep: number, // when failing to set aspect ratio, increase threshold by this much
|
|
||||||
increaseAfterConsecutiveResets: number // increase if AR resets this many times in a row
|
|
||||||
},
|
|
||||||
sampling: {
|
|
||||||
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
|
|
||||||
randomCols: number, // we add this many randomly selected columns to the static columns
|
|
||||||
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks
|
|
||||||
},
|
|
||||||
guardLine: { // all pixels on the guardline need to be black, or else we trigger AR recalculation
|
|
||||||
// (if AR fails to be recalculated, we reset AR)
|
|
||||||
enabled: boolean,
|
|
||||||
ignoreEdgeMargin: number, // we ignore anything that pokes over the black line this close to the edge
|
|
||||||
// (relative to width of the sample)
|
|
||||||
imageTestThreshold: number, // when testing for image, this much pixels must be over blackbarThreshold
|
|
||||||
edgeTolerancePx: number, // black edge violation is performed this far from reported 'last black pixel'
|
|
||||||
edgeTolerancePercent: null // unused. same as above, except use % of canvas height instead of pixels
|
|
||||||
},
|
|
||||||
fallbackMode: {
|
|
||||||
enabled: boolean,
|
|
||||||
safetyBorderPx: number, // determines the thickness of safety border in fallback mode
|
|
||||||
noTriggerZonePx: number // if we detect edge less than this many pixels thick, we don't correct.
|
|
||||||
},
|
|
||||||
arSwitchLimiter: { // to be implemented
|
|
||||||
switches: number, // we can switch this many times
|
|
||||||
period: number // per this period
|
|
||||||
},
|
|
||||||
edgeDetection: {
|
|
||||||
sampleWidth: number, // we take a sample this wide for edge detection
|
|
||||||
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
|
|
||||||
confirmationThreshold: number, //
|
|
||||||
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
|
|
||||||
// to confirm an edge in case there's no edges on top or bottom (other
|
|
||||||
// than logo, of course)
|
|
||||||
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
|
|
||||||
// or watermark.
|
|
||||||
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
|
|
||||||
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
|
|
||||||
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
|
|
||||||
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
|
|
||||||
// continue with search. It's pointless, because black edge is higher/lower than we
|
|
||||||
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
|
|
||||||
},
|
|
||||||
pillarTest: {
|
|
||||||
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
|
|
||||||
allowMisaligned: number // left and right edge can vary this much (%)
|
|
||||||
},
|
|
||||||
textLineTest: {
|
|
||||||
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
|
|
||||||
// with text. This value is relative to canvas width (%)
|
|
||||||
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
|
|
||||||
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
|
|
||||||
// is over 50% of the canvas width
|
|
||||||
testRowOffset: number // we test this % of height from detected edge
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
restrictions?: RestrictionsSettings;
|
restrictions?: RestrictionsSettings;
|
||||||
|
|
||||||
@ -195,7 +353,7 @@ interface SettingsInterface {
|
|||||||
conditionalDifferencePercent: number // black bars less than this wide will trigger stretch
|
conditionalDifferencePercent: number // black bars less than this wide will trigger stretch
|
||||||
// if mode is set to '1'. 1.0=100%
|
// if mode is set to '1'. 1.0=100%
|
||||||
},
|
},
|
||||||
kbm: {
|
kbm: { // TODO: we dont use this anymore
|
||||||
enabled: boolean, // if keyboard/mouse handler service will run
|
enabled: boolean, // if keyboard/mouse handler service will run
|
||||||
keyboardEnabled: boolean, // if keyboard shortcuts are processed
|
keyboardEnabled: boolean, // if keyboard shortcuts are processed
|
||||||
mouseEnabled: boolean, // if mouse movement is processed
|
mouseEnabled: boolean, // if mouse movement is processed
|
||||||
@ -230,6 +388,7 @@ interface SettingsInterface {
|
|||||||
pan?: any,
|
pan?: any,
|
||||||
version?: string,
|
version?: string,
|
||||||
preventReload?: boolean,
|
preventReload?: boolean,
|
||||||
|
lastModified?: Date,
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// ::: MITIGATIONS :::
|
// ::: MITIGATIONS :::
|
||||||
@ -242,33 +401,6 @@ interface SettingsInterface {
|
|||||||
limit?: number,
|
limit?: number,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// -----------------------------------------
|
|
||||||
// ::: ACTIONS :::
|
|
||||||
// -----------------------------------------
|
|
||||||
// Nastavitve za ukaze. Zamenja stare nastavitve za bližnične tipke.
|
|
||||||
//
|
|
||||||
// Polje 'shortcut' je tabela, če se slučajno lotimo kdaj delati choordov.
|
|
||||||
actions: {
|
|
||||||
name?: string, // name displayed in settings
|
|
||||||
label?: string, // name displayed in ui (can be overridden in scope/playerUi)
|
|
||||||
cmd?: {
|
|
||||||
action: string,
|
|
||||||
arg: any,
|
|
||||||
customArg?: any,
|
|
||||||
persistent?: boolean, // optional, false by default. If true, change doesn't take effect immediately.
|
|
||||||
// Instead, this action saves stuff to settings
|
|
||||||
}[],
|
|
||||||
scopes?: {
|
|
||||||
global?: ActionScopeInterface,
|
|
||||||
site?: ActionScopeInterface,
|
|
||||||
page?: ActionScopeInterface
|
|
||||||
},
|
|
||||||
playerUi?: {
|
|
||||||
show: boolean,
|
|
||||||
path?: string,
|
|
||||||
},
|
|
||||||
userAdded?: boolean,
|
|
||||||
}[],
|
|
||||||
// This object fulfills the same purpose as 'actions', but is written in less retarded and overly
|
// This object fulfills the same purpose as 'actions', but is written in less retarded and overly
|
||||||
// complicated way. Hopefully it'll be easier to maintain it that way.
|
// complicated way. Hopefully it'll be easier to maintain it that way.
|
||||||
commands?: {
|
commands?: {
|
||||||
@ -279,6 +411,7 @@ interface SettingsInterface {
|
|||||||
internal?: CommandInterface[],
|
internal?: CommandInterface[],
|
||||||
},
|
},
|
||||||
whatsNewChecked: boolean,
|
whatsNewChecked: boolean,
|
||||||
|
newFeatureTracker: any,
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// ::: SITE CONFIGURATION :::
|
// ::: SITE CONFIGURATION :::
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
@ -310,68 +443,30 @@ interface SettingsInterface {
|
|||||||
sites: {
|
sites: {
|
||||||
[x: string]: SiteSettingsInterface,
|
[x: string]: SiteSettingsInterface,
|
||||||
}
|
}
|
||||||
// sites: {
|
|
||||||
// [x: string]: {
|
|
||||||
// defaultCrop?: any, // v6 new
|
|
||||||
// defaultStretch?: any, // v6 new
|
|
||||||
// enabled: ExtensionEnvironmentSettingsInterface, // v6 new
|
|
||||||
// enabledAard: ExtensionEnvironmentSettingsInterface,// v6 new
|
|
||||||
|
|
||||||
// // everything 'superseded by' needs to be implemented
|
|
||||||
// // as well as ported from the old settings
|
|
||||||
// mode?: ExtensionMode, // v6 — superseded by looking at enableIn
|
|
||||||
// autoar?: ExtensionMode, // v6 — superseded by looking at enableIn
|
|
||||||
// autoarFallback?: ExtensionMode, // v6 — deprecated, no replacement
|
|
||||||
// stretch?: StretchType, // v6 — superseded by defaultStretch
|
|
||||||
// videoAlignment?: VideoAlignmentType,
|
|
||||||
// keyboardShortcutsEnabled?: ExtensionMode,
|
|
||||||
// type?: string,
|
|
||||||
// override?: boolean,
|
|
||||||
// arPersistence?: boolean,
|
|
||||||
// actions?: any;
|
|
||||||
|
|
||||||
// cropModePersistence?: CropModePersistence;
|
|
||||||
|
|
||||||
// DOM?: {
|
|
||||||
// player?: {
|
|
||||||
// manual?: boolean,
|
|
||||||
// querySelectors?: string,
|
|
||||||
// additionalCss?: string,
|
|
||||||
// useRelativeAncestor?: boolean,
|
|
||||||
// videoAncestor?: any,
|
|
||||||
// playerNodeCss?: string,
|
|
||||||
// periodicallyRefreshPlayerElement?: boolean
|
|
||||||
// },
|
|
||||||
// video?: {
|
|
||||||
// manual?: boolean,
|
|
||||||
// querySelectors?: string,
|
|
||||||
// additionalCss?: string,
|
|
||||||
// useRelativeAncestor?: boolean,
|
|
||||||
// playerNodeCss?: string
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// css?: string;
|
|
||||||
// usePlayerArInFullscreen?: boolean;
|
|
||||||
|
|
||||||
// restrictions?: RestrictionsSettings;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SiteSettingsInterface {
|
export interface SiteSettingsInterface {
|
||||||
enable: ExtensionEnvironmentSettingsInterface;
|
notes?: string; // any special things related to this site.
|
||||||
enableAard: ExtensionEnvironmentSettingsInterface;
|
|
||||||
enableKeyboard: ExtensionEnvironmentSettingsInterface;
|
|
||||||
|
|
||||||
type?: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
|
enable: ExtensionMode;
|
||||||
defaultType: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
|
enableAard: ExtensionMode;
|
||||||
|
enableKeyboard: InputHandlingMode;
|
||||||
|
enableUI: ExtensionMode;
|
||||||
|
|
||||||
|
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy; // presumed to be 'Use as default' if not defined
|
||||||
|
overrideWhenEmbedded?: EmbeddedContentSettingsOverridePolicy;
|
||||||
|
|
||||||
|
|
||||||
|
autocreated?: boolean;
|
||||||
|
type?: SiteSupportLevel;
|
||||||
|
defaultType: SiteSupportLevel;
|
||||||
|
|
||||||
// must be defined in @global and @empty
|
// must be defined in @global and @empty
|
||||||
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment
|
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment
|
||||||
|
|
||||||
defaults?: { // must be defined in @global and @empty
|
defaults?: { // must be defined in @global and @empty
|
||||||
crop?: {type: AspectRatioType, [x: string]: any},
|
crop?: {type: AspectRatioType, [x: string]: any},
|
||||||
stretch?: StretchType,
|
stretch?: {type: StretchType, ratio?: number},
|
||||||
alignment?: {x: VideoAlignmentType, y: VideoAlignmentType},
|
alignment?: {x: VideoAlignmentType, y: VideoAlignmentType},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,6 +474,7 @@ export interface SiteSettingsInterface {
|
|||||||
stretchModePersistence?: CropModePersistence;
|
stretchModePersistence?: CropModePersistence;
|
||||||
alignmentPersistence?: CropModePersistence;
|
alignmentPersistence?: CropModePersistence;
|
||||||
|
|
||||||
|
playerAutoConfig?: PlayerAutoConfigInterface;
|
||||||
|
|
||||||
activeDOMConfig?: string;
|
activeDOMConfig?: string;
|
||||||
DOMConfig?: { [x: string]: SiteDOMSettingsInterface };
|
DOMConfig?: { [x: string]: SiteDOMSettingsInterface };
|
||||||
@ -391,12 +487,17 @@ export interface SiteSettingsInterface {
|
|||||||
override?: boolean; // whether settings for this site will be overwritten by extension upgrade script
|
override?: boolean; // whether settings for this site will be overwritten by extension upgrade script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlayerAutoConfigInterface {
|
||||||
|
modified: boolean;
|
||||||
|
initialIndex: number;
|
||||||
|
currentIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SiteDOMSettingsInterface {
|
export interface SiteDOMSettingsInterface {
|
||||||
type: 'official' | 'community' | 'user-defined' | 'modified' | undefined;
|
type: SiteSupportLevel; // 'official' | 'community' | 'user-defined' | 'modified' | undefined;
|
||||||
elements?: {
|
elements?: {
|
||||||
player?: SiteDOMElementSettingsInterface,
|
player?: PlayerDOMSettingsInterface,
|
||||||
video?: SiteDOMElementSettingsInterface,
|
video?: PlayerDOMSettingsInterface,
|
||||||
other?: { [x: number]: SiteDOMElementSettingsInterface }
|
|
||||||
};
|
};
|
||||||
customCss?: string;
|
customCss?: string;
|
||||||
periodicallyRefreshPlayerElement?: boolean;
|
periodicallyRefreshPlayerElement?: boolean;
|
||||||
@ -407,12 +508,18 @@ export interface SiteDOMSettingsInterface {
|
|||||||
anchorElement?: HTMLElement;
|
anchorElement?: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlayerDOMSettingsInterface {
|
||||||
|
detectionMode: PlayerDetectionMode,
|
||||||
|
allowAutoFallback: boolean,
|
||||||
|
ancestorIndex?: number,
|
||||||
|
querySelectors?: string,
|
||||||
|
}
|
||||||
|
|
||||||
export interface SiteDOMElementSettingsInterface {
|
export interface SiteDOMElementSettingsInterface {
|
||||||
manual?: boolean;
|
manual?: boolean;
|
||||||
querySelectors?: string;
|
querySelectors?: string;
|
||||||
index?: number; // previously: useRelativeAncestor + videoAncestor
|
index?: number; // previously: useRelativeAncestor + videoAncestor
|
||||||
mode?: 'index' | 'qs';
|
mode?: 'index' | 'qs';
|
||||||
nodeCss?: {[x: string]: string};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SettingsInterface;
|
export default SettingsInterface;
|
||||||
|
|||||||
7
src/common/interfaces/StretchInterface.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import StretchType from '../enums/StretchType.enum';
|
||||||
|
|
||||||
|
export interface Stretch {
|
||||||
|
type: StretchType,
|
||||||
|
ratio?: number,
|
||||||
|
limit?: number,
|
||||||
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<p><i><a href="https://www.youtube.com/watch?v=Mn3YEJTSYs8&feature=youtu.be&t=770" target='_blank'>...</a> but as is normal, you can't use a free extensions without the developers begging for money. It's the bi-daily beggathon here on ... wherever here is.</i>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Jokes and references few will get aside, developing this extension does take a decent amount of time, motivation, carefully calibrated quantities
|
|
||||||
of alcohol and enough coffee to bankrupt a small nation.
|
|
||||||
</p>
|
|
||||||
<p>If you want to buy me a beer or bankroll my caffeine addiction, you can do so by <a href="https://paypal.me/tamius">clicking here</a>. All donations are appreciated.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import Debug from '../conf/Debug';
|
import Debug from '../../ext/conf/Debug';
|
||||||
|
|
||||||
class ObjectCopy {
|
class ObjectCopy {
|
||||||
static addNew(current, newValues){
|
static addNew(current, newValues){
|
||||||
@ -1,7 +1,3 @@
|
|||||||
export async function sleep(timeout) {
|
|
||||||
return new Promise<void>( (resolve, reject) => setTimeout(() => resolve(), timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates deep copy of an object
|
* Creates deep copy of an object
|
||||||
* @param obj
|
* @param obj
|
||||||
12
src/common/utils/comparators.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export function collectionHas(collection, element): boolean {
|
||||||
|
for (let i = 0, len = collection.length; i < len; i++) {
|
||||||
|
if (collection[i] == element) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function equalish(a: number,b: number, tolerance: number): boolean {
|
||||||
|
return a > b - tolerance && a < b + tolerance;
|
||||||
|
}
|
||||||
103
src/common/utils/getElementStyles.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
export type ProcessedElementStyles = {
|
||||||
|
[x: string]: {
|
||||||
|
css: string,
|
||||||
|
pxValue: number
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that this function is written _very_ dangerously
|
||||||
|
* and includes absolutely no error handling
|
||||||
|
*/
|
||||||
|
function getPixelValue(value: string, element?: HTMLElement, prop?: string) {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.endsWith('px')) {
|
||||||
|
// console.log('value ends in px:', value)
|
||||||
|
return parseFloat(value);
|
||||||
|
}
|
||||||
|
if (value.endsWith('%')) {
|
||||||
|
// console.log('value ends in %:', value)
|
||||||
|
// This allegedly doesn't work for certain types of properties, allegedly.
|
||||||
|
const parent = element?.parentElement;
|
||||||
|
if (parent && prop) {
|
||||||
|
const parentDimensions = parent.getBoundingClientRect();
|
||||||
|
return (parseFloat(value) * 0.01) * (prop.includes('height') || ['top', 'bottom'].includes(prop) ? parentDimensions.height : parentDimensions.width);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.endsWith("vw")) {
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
return (parseFloat(value) / 100) * viewportWidth;
|
||||||
|
}
|
||||||
|
if (value.endsWith("vh")) {
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
return (parseFloat(value) / 100) * viewportHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.endsWith("rem")) {
|
||||||
|
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
return rootFontSize * parseFloat(value);
|
||||||
|
}
|
||||||
|
if (value.endsWith("em")) {
|
||||||
|
const fontSize = parseFloat(getComputedStyle(element).fontSize);
|
||||||
|
return fontSize * parseFloat(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function getElementStyles(element: HTMLElement, props: string[], theoryProps?: string[]): ProcessedElementStyles {
|
||||||
|
const stylesheets = document.styleSheets;
|
||||||
|
const computedStyles = getComputedStyle(element);
|
||||||
|
const stylesOut = {};
|
||||||
|
|
||||||
|
for (const stylesheet of stylesheets) {
|
||||||
|
// console.log('——————————————— processing stylesheet:', stylesheet);
|
||||||
|
try {
|
||||||
|
for (const rule of stylesheet?.cssRules) {
|
||||||
|
if (rule instanceof CSSStyleRule) {
|
||||||
|
if (element.matches(rule.selectorText)) {
|
||||||
|
// console.log('element matches rule:', rule);
|
||||||
|
|
||||||
|
for (const property in rule.style) {
|
||||||
|
if (!props.includes(property)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cssValue = rule.style.getPropertyValue(property);
|
||||||
|
|
||||||
|
if (theoryProps?.includes(property)) {
|
||||||
|
stylesOut[property] = {css: cssValue};
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualValue = computedStyles.getPropertyValue(property);
|
||||||
|
|
||||||
|
const theory = getPixelValue(cssValue, element, property);
|
||||||
|
const practice = getPixelValue(actualValue, element, property);
|
||||||
|
|
||||||
|
if (theory && practice && (Math.abs(theory - practice) < 1)) {
|
||||||
|
stylesOut[property] = {
|
||||||
|
css: cssValue,
|
||||||
|
pxValue: theory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
// Cross-origin styles amy cause problems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stylesOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
49
src/common/utils/getHost.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Reqzests iframe parent hostname
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getIframeParentHost(): Promise<string> {
|
||||||
|
return new Promise<any>((resolve) => {
|
||||||
|
let resolved = false;
|
||||||
|
let resendInterval;
|
||||||
|
|
||||||
|
function handleMessage(event) {
|
||||||
|
if (event.data.action === 'uw-parent-hostname') {
|
||||||
|
resolved = true;
|
||||||
|
clearInterval(resendInterval);
|
||||||
|
|
||||||
|
window.removeEventListener('message', handleMessage);
|
||||||
|
resolve(event.data.hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', handleMessage);
|
||||||
|
resendInterval = setInterval(
|
||||||
|
() => {
|
||||||
|
if (!resolved) {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{ action: 'uw-get-parent-hostname' },
|
||||||
|
'*'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
500
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleMessage(event) {
|
||||||
|
if (event.data.action === 'uw-get-parent-hostname') {
|
||||||
|
event.source.postMessage(
|
||||||
|
{action: 'uw-parent-hostname', hostname: window.location.hostname},
|
||||||
|
'*' as any
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setupHostnameReporting() {
|
||||||
|
window.removeEventListener('message', handleMessage); // setupHostnameReporting may run more than once
|
||||||
|
window.addEventListener('message', handleMessage);
|
||||||
|
}
|
||||||
@ -1,69 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="uw-clickthrough relative w-100 h-100">
|
|
||||||
<template v-for="rectangle of drawnRectangles" :key="rectangle.id ?? rectangle">
|
|
||||||
|
|
||||||
<!-- Player element overlays -->
|
|
||||||
<div class="absolute z-index-overlay"
|
|
||||||
:style="rectangle.style"
|
|
||||||
>
|
|
||||||
<!-- used for drawing player element overlay rectangles - keep this section empty -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Notification overlay -->
|
|
||||||
<div class="absolute z-index-notification notification-area">
|
|
||||||
<template v-for="notification of displayedNotifications" :key="notification.id">
|
|
||||||
<div class="notification d-flex flex-row">
|
|
||||||
<div class="notification-icon">
|
|
||||||
<mdicon :name="notification.icon ?? 'alert'" :size="128"></mdicon>
|
|
||||||
</div>
|
|
||||||
<div class="notification-text">
|
|
||||||
<h3 class="notification-title">{{ notification.title }}</h3>
|
|
||||||
<p class="notification-verbose">{{ notification.text }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import UIProbeMixin from './src/utils/UIProbeMixin';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
UIProbeMixin
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
drawnRectangles: [],
|
|
||||||
displayedNotifications: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
this.logger = new Logger();
|
|
||||||
|
|
||||||
// this prolly needs to be taken out
|
|
||||||
await this.logger.init({
|
|
||||||
allowLogging: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup the "companion" onMouseMove handler to the one in the content script.
|
|
||||||
* We can handle events with the same function we use to handle events from
|
|
||||||
* the content script.
|
|
||||||
*/
|
|
||||||
document.addEventListener('mousemove', (event) => {
|
|
||||||
this.handleProbe({
|
|
||||||
coords: {
|
|
||||||
x: event.clientX,
|
|
||||||
y: event.clientY
|
|
||||||
}
|
|
||||||
}, this.origin);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,345 +0,0 @@
|
|||||||
<template>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="settingsInitialized && uwTriggerZoneVisible && !isGlobal"
|
|
||||||
class="uw-hover uv-hover-trigger-region uw-clickable"
|
|
||||||
:style="uwTriggerRegionConf"
|
|
||||||
@mouseenter="showUwWindow"
|
|
||||||
>
|
|
||||||
<h1>Aspect ratio controls</h1>
|
|
||||||
<div>Hover to activate</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="settingsInitialized && uwWindowVisible"
|
|
||||||
class="uw-window flex flex-col uw-clickable"
|
|
||||||
:class="{'fade-out': uwWindowFadeOut}"
|
|
||||||
@mouseenter="cancelUwWindowHide"
|
|
||||||
@mouseleave="hideUwWindow()"
|
|
||||||
>
|
|
||||||
<PlayerUIWindow
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:logger="logger"
|
|
||||||
:in-player="!isGlobal"
|
|
||||||
:site="site"
|
|
||||||
@close="uwWindowVisible = false"
|
|
||||||
@preventClose="(event) => uwWindowFadeOutDisabled = event"
|
|
||||||
></PlayerUIWindow>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import PlayerUIWindow from './src/PlayerUIWindow.vue'
|
|
||||||
import BrowserDetect from '../ext/conf/BrowserDetect';
|
|
||||||
import Logger from '../ext/lib/Logger';
|
|
||||||
import Settings from '../ext/lib/Settings';
|
|
||||||
import EventBus from '../ext/lib/EventBus';
|
|
||||||
import UIProbeMixin from './src/utils/UIProbeMixin';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
PlayerUIWindow
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
UIProbeMixin
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
uwTriggerZoneVisible: false,
|
|
||||||
uwTriggerZoneTimeout: undefined,
|
|
||||||
uwTriggerRegionConf: {
|
|
||||||
left: "10%",
|
|
||||||
top: "10%",
|
|
||||||
height: "30%",
|
|
||||||
width: "30%",
|
|
||||||
maxWidth: "24rem",
|
|
||||||
maxHeight: "13.37rem",
|
|
||||||
},
|
|
||||||
|
|
||||||
uwWindowFadeOutDisabled: false,
|
|
||||||
uwWindowFadeOut: false,
|
|
||||||
uwWindowCloseTimeout: undefined,
|
|
||||||
uwWindowVisible: false,
|
|
||||||
|
|
||||||
// component properties
|
|
||||||
settings: {},
|
|
||||||
BrowserDetect: BrowserDetect,
|
|
||||||
settingsInitialized: false,
|
|
||||||
eventBus: new EventBus(),
|
|
||||||
logger: null,
|
|
||||||
|
|
||||||
// NOTE: chromium doesn't allow us to access window.parent.location
|
|
||||||
// meaning we will have to correct this value from our uwui-probe
|
|
||||||
// messages ... which is a bummer.
|
|
||||||
site: null,
|
|
||||||
origin: '*', // will be set appropriately once the first uwui-probe event is received
|
|
||||||
lastProbeTs: null,
|
|
||||||
|
|
||||||
isGlobal: true,
|
|
||||||
disabled: false,
|
|
||||||
|
|
||||||
uiVisible: true,
|
|
||||||
debugData: {
|
|
||||||
resizer: {},
|
|
||||||
player: {},
|
|
||||||
},
|
|
||||||
debugDataPrettified: '',
|
|
||||||
|
|
||||||
// in global overlay, this property is used to determine
|
|
||||||
// if closing the window should emit uw-set-ui-state
|
|
||||||
// event on eventBus
|
|
||||||
showPlayerUIAfterClose: false,
|
|
||||||
|
|
||||||
statusFlags: {
|
|
||||||
hasDrm: undefined,
|
|
||||||
},
|
|
||||||
|
|
||||||
saveState: {},
|
|
||||||
|
|
||||||
selectedTab: 'videoSettings',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// LPT: NO ARROW FUNCTIONS IN COMPUTED,
|
|
||||||
// IS SUPER HARAM
|
|
||||||
// THINGS WILL NOT WORK IF YOU USE ARROWS
|
|
||||||
windowWidth() {
|
|
||||||
return window.innerWidth;
|
|
||||||
},
|
|
||||||
windowHeight() {
|
|
||||||
return window.innerHeight;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
showUi(visible) {
|
|
||||||
if (visible !== undefined) {
|
|
||||||
this.uiVisible = visible;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resizerDebugData(newData) {
|
|
||||||
this.debugData.resizer = newData;
|
|
||||||
this.debugDataPrettified = JSON.stringify(this.debugData, null, 2);
|
|
||||||
},
|
|
||||||
playerDebugData(newData) {
|
|
||||||
this.debugData.player = newData;
|
|
||||||
this.debugDataPrettified = JSON.stringify(this.debugData, null, 2);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
this.logger = new Logger();
|
|
||||||
|
|
||||||
// this prolly needs to be taken out
|
|
||||||
await this.logger.init({
|
|
||||||
allowLogging: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.settings = new Settings({afterSettingsSaved: this.updateConfig, logger: this.logger});
|
|
||||||
await this.settings.init();
|
|
||||||
this.settingsInitialized = true;
|
|
||||||
|
|
||||||
// set up communication with client script.
|
|
||||||
// NOTE: companion onmousemove is set up in UIProbeMixin
|
|
||||||
window.addEventListener('message', event => {
|
|
||||||
this.handleMessage(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.eventBus.subscribe('uw-config-broadcast', {function: (data) => {
|
|
||||||
if (data.type === 'drm-status') {
|
|
||||||
this.statusFlags.hasDrm = data.hasDrm;
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
|
|
||||||
|
|
||||||
this.eventBus.subscribe('uw-set-ui-state', { function: (data) => {
|
|
||||||
if (data.globalUiVisible !== undefined) {
|
|
||||||
if (this.isGlobal) {
|
|
||||||
if (data.globalUiVisible) {
|
|
||||||
this.showUwWindow();
|
|
||||||
} else {
|
|
||||||
this.hideUwWindow(true);
|
|
||||||
}
|
|
||||||
// this.showPlayerUIAfterClose = data.showPlayerUIAfterClose;
|
|
||||||
} else {
|
|
||||||
// non global UIs are hidden while global overlay
|
|
||||||
// is visible and vice versa
|
|
||||||
// this.disabled = data.globalUiVisible;
|
|
||||||
this.saveState = {
|
|
||||||
uwWindowVisible: this.uwWindowVisible,
|
|
||||||
uwWindowFadeOutDisabled: this.uwWindowFadeOutDisabled,
|
|
||||||
uwWindowFadeOut: this.uwWindowFadeOut
|
|
||||||
};
|
|
||||||
this.uwWindowFadeOutDisabled = false;
|
|
||||||
this.hideUwWindow(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'uw-restore-ui-state',
|
|
||||||
{
|
|
||||||
function: (data) => {
|
|
||||||
if (this.saveState) {
|
|
||||||
if (this.saveState.uwWindowVisible) {
|
|
||||||
this.showUwWindow();
|
|
||||||
}
|
|
||||||
this.uwWindowFadeOutDisabled = this.saveState.uwWindowFadeOutDisabled;
|
|
||||||
this.uwWindowFadeOut = this.saveState.uwWindowFadeOut;
|
|
||||||
}
|
|
||||||
this.saveState = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.sendToParentLowLevel('uwui-get-role', null);
|
|
||||||
this.sendToParentLowLevel('uwui-get-theme', null);
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Gets URL of the browser settings page (i think?)
|
|
||||||
*/
|
|
||||||
getUrl(url) {
|
|
||||||
return BrowserDetect.getURL(url);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mostly intended to process messages received via window.addEventListener('message').
|
|
||||||
* This method should include minimal logic — instead, it should only route messages
|
|
||||||
* to the correct function down the line.
|
|
||||||
*/
|
|
||||||
handleMessage(event) {
|
|
||||||
switch (event.data.action) {
|
|
||||||
case 'uwui-probe':
|
|
||||||
if (!this.site) {
|
|
||||||
this.origin = event.origin;
|
|
||||||
this.site = event.origin.split('//')[1];
|
|
||||||
}
|
|
||||||
return this.handleProbe(event.data, event.origin); // handleProbe is defined in UIProbeMixin
|
|
||||||
case 'uw-bus-tunnel':
|
|
||||||
return this.handleBusTunnelIn(event.data.payload);
|
|
||||||
case 'uwui-set-role':
|
|
||||||
this.isGlobal = event.data.payload.role === 'global';
|
|
||||||
this.sendToParentLowLevel('uwui-interface-ready', true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends message to parent _without_ using event bus.
|
|
||||||
*/
|
|
||||||
sendToParentLowLevel(action, payload, lowLevelExtras = {}) {
|
|
||||||
window.parent.postMessage(
|
|
||||||
{
|
|
||||||
action, payload, ...lowLevelExtras
|
|
||||||
},
|
|
||||||
'*'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
showUwWindow() {
|
|
||||||
this.uwWindowFadeOut = false;
|
|
||||||
this.uwWindowVisible = true;
|
|
||||||
this.uwTriggerZoneVisible = false;
|
|
||||||
|
|
||||||
// refresh DRM status
|
|
||||||
this.eventBus.send('get-drm-status');
|
|
||||||
|
|
||||||
// if (this.isGlobal) {
|
|
||||||
// this.sendToParentLowLevel('uwui-clickable', undefined, {clickable: true});
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
|
|
||||||
hideUwWindow(skipTimeout = false) {
|
|
||||||
if (this.uwWindowFadeOutDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeout = skipTimeout ? 0 : 1100;
|
|
||||||
|
|
||||||
this.uwWindowCloseTimeout = setTimeout(
|
|
||||||
() => {
|
|
||||||
this.uwWindowVisible = false;
|
|
||||||
|
|
||||||
// Global UI has some extra housekeeping to do when window gets hidden
|
|
||||||
if (this.isGlobal) {
|
|
||||||
this.sendToParentLowLevel('uwui-global-window-hidden', {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
timeout
|
|
||||||
);
|
|
||||||
this.uwWindowFadeOut = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelUwWindowHide() {
|
|
||||||
this.uwWindowFadeOut = false;
|
|
||||||
clearTimeout(this.uwWindowCloseTimeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleBusTunnelIn(payload) {
|
|
||||||
this.eventBus.send(payload.action, payload.config, payload.routingData);
|
|
||||||
},
|
|
||||||
|
|
||||||
selectTab(tab) {
|
|
||||||
this.selectedTab = tab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import 'res/css/uwui-base.scss';
|
|
||||||
@import 'res/css/colors.scss';
|
|
||||||
@import 'res/css/font/overpass.css';
|
|
||||||
@import 'res/css/font/overpass-mono.css';
|
|
||||||
@import 'res/css/common.scss';
|
|
||||||
@import './src/res-common/_variables';
|
|
||||||
|
|
||||||
|
|
||||||
.uw-hover {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
z-index: 999999999999999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uv-hover-trigger-region {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
border: 0.5rem dashed #fff;
|
|
||||||
color: #fff;
|
|
||||||
backdrop-filter: blur(0.5rem) brightness(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.uw-window {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 10%;
|
|
||||||
left: 10%;
|
|
||||||
|
|
||||||
z-index: 999999999999999999;
|
|
||||||
|
|
||||||
width: 2500px;
|
|
||||||
height: 1200px;
|
|
||||||
max-width: 80%;
|
|
||||||
max-height: 80%;
|
|
||||||
|
|
||||||
pointer-events: all !important;
|
|
||||||
|
|
||||||
opacity: 1;
|
|
||||||
backdrop-filter: blur(16px) saturate(120%);
|
|
||||||
|
|
||||||
&.fade-out {
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
transition-delay: 0.5s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,535 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="popup-panel">
|
|
||||||
<!--
|
|
||||||
NOTE — the code that makes ultrawidify popup work in firefox regardless of whether the
|
|
||||||
extension is being displayed in a normal or a small/overflow popup breaks the popup
|
|
||||||
behaviour on Chrome (where the popup would never reach the full width of 800px)
|
|
||||||
|
|
||||||
Since I'm tired and the hour is getting late, we'll just add an extra CSS class for
|
|
||||||
non-firefox builds of this extension and be done with it. No need to complicate things
|
|
||||||
further than that.
|
|
||||||
-->
|
|
||||||
<div v-if="settingsInitialized"
|
|
||||||
class="popup flex flex-col no-overflow"
|
|
||||||
:class="{'popup-chrome': ! BrowserDetect?.firefox}"
|
|
||||||
>
|
|
||||||
<div class="flex-row flex-nogrow flex-noshrink relative header"
|
|
||||||
>
|
|
||||||
<div class="grow shrink">
|
|
||||||
<h1>
|
|
||||||
<span class="smallcaps">Ultrawidify</span>: <small>Quick settings</small>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div v-if="BrowserDetect?.processEnvChannel !== 'stable'" class="absolute channel-info version-info">
|
|
||||||
Build channel: {{BrowserDetect?.processEnvChannel}} <br/>
|
|
||||||
<label>Version:</label> <br/>
|
|
||||||
{{ settings.getExtensionVersion() }}
|
|
||||||
</div>
|
|
||||||
<div v-else class="version-info">
|
|
||||||
<label>Version:</label> <br/>
|
|
||||||
{{ settings.getExtensionVersion() }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- CONTAINER ROOT -->
|
|
||||||
<div class="flex flex-row body no-overflow flex-grow">
|
|
||||||
|
|
||||||
<!-- TABS -->
|
|
||||||
<div class="flex flex-col tab-row" style="flex: 3 3; border-right: 1px solid #222;">
|
|
||||||
<div
|
|
||||||
v-for="tab of tabs"
|
|
||||||
:key="tab.id"
|
|
||||||
class="tab flex flex-row"
|
|
||||||
:class="{'active': tab.id === selectedTab}"
|
|
||||||
@click="selectTab(tab.id)"
|
|
||||||
>
|
|
||||||
<div class="icon-container">
|
|
||||||
<mdicon
|
|
||||||
:name="tab.icon"
|
|
||||||
:size="32"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="label">
|
|
||||||
{{tab.label}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CONTENT -->
|
|
||||||
<div class="scrollable" style="flex: 7 7; padding: 1rem;">
|
|
||||||
<template v-if="settings && siteSettings">
|
|
||||||
<InPlayerUIAdvertisement
|
|
||||||
v-if="selectedTab === 'playerUiCtl'"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
/>
|
|
||||||
<PopupVideoSettings
|
|
||||||
v-if="selectedTab === 'videoSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
></PopupVideoSettings>
|
|
||||||
<!-- <PlayerDetectionPanel
|
|
||||||
v-if="selectedTab === 'playerDetection'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:site="site.host"
|
|
||||||
>
|
|
||||||
</PlayerDetectionPanel> -->
|
|
||||||
<BaseExtensionSettings
|
|
||||||
v-if="selectedTab === 'extensionSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:site="site.host"
|
|
||||||
>
|
|
||||||
</BaseExtensionSettings>
|
|
||||||
</template>
|
|
||||||
<template v-else>No settings or site settings found.</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import BaseExtensionSettings from './src/PlayerUiPanels/BaseExtensionSettings.vue'
|
|
||||||
import PlayerDetectionPanel from './src/PlayerUiPanels/PlayerDetectionPanel.vue'
|
|
||||||
import PopupVideoSettings from './src/popup/panels/PopupVideoSettings.vue'
|
|
||||||
import InPlayerUIAdvertisement from './src/PlayerUiPanels/InPlayerUiAdvertisement.vue';
|
|
||||||
import Debug from '../ext/conf/Debug';
|
|
||||||
import BrowserDetect from '../ext/conf/BrowserDetect';
|
|
||||||
import Comms from '../ext/lib/comms/Comms';
|
|
||||||
import CommsClient, {CommsOrigin} from '../ext/lib/comms/CommsClient';
|
|
||||||
import Settings from '../ext/lib/Settings';
|
|
||||||
import Logger from '../ext/lib/Logger';
|
|
||||||
import EventBus from '../ext/lib/EventBus';
|
|
||||||
import {ChromeShittinessMitigations as CSM} from '../common/js/ChromeShittinessMitigations';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
comms: undefined,
|
|
||||||
eventBus: new EventBus(),
|
|
||||||
settings: {},
|
|
||||||
settingsInitialized: false,
|
|
||||||
narrowPopup: null,
|
|
||||||
sideMenuVisible: null,
|
|
||||||
logger: undefined,
|
|
||||||
site: undefined,
|
|
||||||
siteSettings: undefined,
|
|
||||||
selectedTab: 'playerUiCtl',
|
|
||||||
tabs: [
|
|
||||||
// see this for icons: https://pictogrammers.com/library/mdi/
|
|
||||||
{id: 'playerUiCtl', label: 'In-player UI', icon: 'artboard'},
|
|
||||||
{id: 'videoSettings', label: 'Video settings', icon: 'crop'},
|
|
||||||
// {id: 'playerDetection', label: 'Player detection', icon: 'television-play'},
|
|
||||||
{id: 'extensionSettings', label: 'Site and Extension options', icon: 'cogs' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
this.logger = new Logger();
|
|
||||||
await this.logger.init({
|
|
||||||
allowLogging: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.settings = new Settings({afterSettingsSaved: () => this.updateConfig(), logger: this.logger});
|
|
||||||
await this.settings.init();
|
|
||||||
this.settingsInitialized = true;
|
|
||||||
|
|
||||||
// const port = chrome.runtime.connect({name: 'popup-port'});
|
|
||||||
// port.onMessage.addListener( (m,p) => this.processReceivedMessage(m,p));
|
|
||||||
// CSM.setProperty('port', port);
|
|
||||||
|
|
||||||
this.eventBus = new EventBus();
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'set-current-site',
|
|
||||||
{
|
|
||||||
function: (config, context) => {
|
|
||||||
if (this.site) {
|
|
||||||
if (!this.site.host) {
|
|
||||||
// dunno why this fix is needed, but sometimes it is
|
|
||||||
this.site.host = config.site.host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.site = config.site;
|
|
||||||
// this.selectedSite = this.selectedSite || config.site.host;
|
|
||||||
this.siteSettings = this.settings.getSiteSettings(this.site.host);
|
|
||||||
|
|
||||||
this.eventBus.setupPopupTunnelWorkaround({
|
|
||||||
origin: CommsOrigin.Popup,
|
|
||||||
comms: {
|
|
||||||
forwardTo: 'active'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadFrames(this.site);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.comms = new CommsClient('popup-port', this.logger, this.eventBus);
|
|
||||||
this.eventBus.setComms(this.comms);
|
|
||||||
this.eventBus.setupPopupTunnelWorkaround({
|
|
||||||
origin: CommsOrigin.Popup,
|
|
||||||
comms: {forwardTo: 'active'}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// ensure we'll clean player markings on popup close
|
|
||||||
window.addEventListener("unload", () => {
|
|
||||||
CSM.port.postMessage({
|
|
||||||
cmd: 'unmark-player',
|
|
||||||
forwardToAll: true,
|
|
||||||
});
|
|
||||||
// if (BrowserDetect.anyChromium) {
|
|
||||||
// chrome.extension.getBackgroundPage().sendUnmarkPlayer({
|
|
||||||
// cmd: 'unmark-player',
|
|
||||||
// forwardToAll: true,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
// get info about current site from background script
|
|
||||||
while (true) {
|
|
||||||
this.requestSite();
|
|
||||||
await this.sleep(5000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async updated() {
|
|
||||||
const body = document.getElementsByTagName('body')[0];
|
|
||||||
|
|
||||||
// ensure that narrowPopup only gets set the first time the popup renders
|
|
||||||
// if popup was rendered before, we don't do anything because otherwise
|
|
||||||
// we'll be causing an unwanted re-render
|
|
||||||
//
|
|
||||||
// another thing worth noting — the popup gets first initialized with
|
|
||||||
// offsetWidth set to 0. This means proper popup will be displayed as a
|
|
||||||
// mini popup if we don't check for that.
|
|
||||||
if (this.narrowPopup === null && body.offsetWidth > 0) {
|
|
||||||
this.narrowPopup = body.offsetWidth < 600;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Debug,
|
|
||||||
BrowserDetect,
|
|
||||||
PopupVideoSettings, PlayerDetectionPanel, BaseExtensionSettings, InPlayerUIAdvertisement
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async sleep(t) {
|
|
||||||
return new Promise( (resolve,reject) => {
|
|
||||||
setTimeout(() => resolve(), t);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toObject(obj) {
|
|
||||||
return JSON.parse(JSON.stringify(obj));
|
|
||||||
},
|
|
||||||
requestSite() {
|
|
||||||
try {
|
|
||||||
this.logger.log('info','popup', '[popup::getSite] Requesting current site ...')
|
|
||||||
// CSM.port.postMessage({command: 'get-current-site'});
|
|
||||||
this.eventBus.send(
|
|
||||||
'get-current-site',
|
|
||||||
{
|
|
||||||
comms: {forwardTo: 'active'}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log('error','popup','[popup::getSite] sending get-current-site failed for some reason. Reason:', e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getRandomColor() {
|
|
||||||
return `rgb(${Math.floor(Math.random() * 128)}, ${Math.floor(Math.random() * 128)}, ${Math.floor(Math.random() * 128)})`;
|
|
||||||
},
|
|
||||||
selectTab(tab) {
|
|
||||||
this.selectedTab = tab;
|
|
||||||
},
|
|
||||||
processReceivedMessage(message, port) {
|
|
||||||
this.logger.log('info', 'popup', '[popup::processReceivedMessage] received message:', message)
|
|
||||||
|
|
||||||
if (message.command === 'set-current-site'){
|
|
||||||
if (this.site) {
|
|
||||||
if (!this.site.host) {
|
|
||||||
// dunno why this fix is needed, but sometimes it is
|
|
||||||
this.site.host = site.tabHostname;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.site = message.site;
|
|
||||||
|
|
||||||
// update activeSites
|
|
||||||
// this.activeSites = this.activeSites.filter(x => x.host !== message.site);
|
|
||||||
|
|
||||||
// add current site
|
|
||||||
// this.activeSites = unshift({
|
|
||||||
// host: message.site.host,
|
|
||||||
// isIFrame: false, // currently unused
|
|
||||||
// });
|
|
||||||
this.selectedSite = this.selectedSite || message.site.host;
|
|
||||||
|
|
||||||
this.loadFrames(this.site);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
isDefaultFrame(frameId) {
|
|
||||||
return frameId === '__playing' || frameId === '__all';
|
|
||||||
},
|
|
||||||
loadFrames() {
|
|
||||||
this.activeSites = [{
|
|
||||||
host: this.site.host,
|
|
||||||
isIFrame: false, // not used tho. Maybe one day
|
|
||||||
}];
|
|
||||||
this.selectedSite = this.selectedSite || this.site.host;
|
|
||||||
|
|
||||||
// for (const frame in videoTab.frames) {
|
|
||||||
// this.activeFrames.push({
|
|
||||||
// id: `${this.site.id}-${frame}`,
|
|
||||||
// label: videoTab.frames[frame].host,
|
|
||||||
// ...this.frameStore[frame],
|
|
||||||
// })
|
|
||||||
|
|
||||||
// // only add each host once at most
|
|
||||||
// if (!this.activeSites.find(x => x.host === videoTab.frames[frame].host)) {
|
|
||||||
// this.activeSites.push({
|
|
||||||
// host: videoTab.frames[frame].host,
|
|
||||||
// isIFrame: undefined // maybe one day
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// update whether video tab can be shown
|
|
||||||
// this.updateCanShowVideoTab();
|
|
||||||
},
|
|
||||||
getRandomColor() {
|
|
||||||
return `rgb(${Math.floor(Math.random() * 128)}, ${Math.floor(Math.random() * 128)}, ${Math.floor(Math.random() * 128)})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
// @import 'res/css/uwui-base.scss';
|
|
||||||
@import 'res/css/colors.scss';
|
|
||||||
@import 'res/css/font/overpass.css';
|
|
||||||
@import 'res/css/font/overpass-mono.css';
|
|
||||||
@import 'res/css/common.scss';
|
|
||||||
@import './src/res-common/_variables';
|
|
||||||
|
|
||||||
.header {
|
|
||||||
background-color: rgb(90, 28, 13);
|
|
||||||
color: #fff;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version-info {
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.8;
|
|
||||||
label {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-support-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.site-support-site {
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-support {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
margin-left: 1rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0rem 1.5rem 0rem 1rem;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
padding: 1rem;
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
transform: translateY(110%);
|
|
||||||
width: 42em;
|
|
||||||
|
|
||||||
background-color: rgba(0,0,0,0.90);
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.tooltip {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.official {
|
|
||||||
background-color: #fa6;
|
|
||||||
color: #000;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #000 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.community {
|
|
||||||
background-color: rgb(85, 85, 179);
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #fff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-support {
|
|
||||||
background-color: rgb(138, 65, 126);
|
|
||||||
color: #eee;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #eee !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.user-added {
|
|
||||||
border: 1px solid #ff0;
|
|
||||||
|
|
||||||
color: #ff0;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #ff0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex-grow: 1;
|
|
||||||
|
|
||||||
.warning-area {
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-content {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-box {
|
|
||||||
background: rgb(255, 174, 107);
|
|
||||||
color: #000;
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-container {
|
|
||||||
margin-right: 1rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: rgba(0,0,0,0.7);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.popup-panel {
|
|
||||||
background-color: rgba(0,0,0,0.50);
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.popup-window-header {
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgba(5,5,5, 0.75);
|
|
||||||
}
|
|
||||||
.tab-row {
|
|
||||||
background-color: rgba(11,11,11, 0.75);
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
padding: 1rem;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
// height: rem;
|
|
||||||
min-height: 3rem;
|
|
||||||
|
|
||||||
border-bottom: 1px solid rgba(128, 128, 128, 0.5);
|
|
||||||
border-top: 1px solid rgba(128, 128, 128, 0.5);
|
|
||||||
opacity: 0.5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
&.active {
|
|
||||||
opacity: 1.0;
|
|
||||||
background-color: $primaryBg;
|
|
||||||
color: rgb(255, 174, 107);
|
|
||||||
border-bottom: 1px solid rgba(116, 78, 47, 0.5);
|
|
||||||
border-top: 1px solid rgba(116, 78, 47, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-container {
|
|
||||||
width: 64px;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-title, .popup-title h1 {
|
|
||||||
font-size: 48px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
border: 1px solid #222 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: 0; padding: 0; font-weight: 400; font-size:24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="color-scheme" content="light dark">
|
|
||||||
<title>Ultrawidify - Content Script User Interface (global overlay)</title>
|
|
||||||
<!-- <link rel="stylesheet" href="csui.css"> -->
|
|
||||||
<% if (NODE_ENV === 'development') { %>
|
|
||||||
<!-- Load some resources only in development environment -->
|
|
||||||
<% } %>
|
|
||||||
</head>
|
|
||||||
<body class="uw-ultrawidify-container-root">
|
|
||||||
<div id="app"></div>
|
|
||||||
<script src="csui-global.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="color-scheme" content="dark">
|
|
||||||
<title>Ultrawidify - Content Script User Interface (in-player overlay)</title>
|
|
||||||
<!-- <link rel="stylesheet" href="csui.css"> -->
|
|
||||||
</head>
|
|
||||||
<body class="uw-ultrawidify-container-root" style="background-color: transparent">
|
|
||||||
<div id="app"></div>
|
|
||||||
<script src="csui.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="color-scheme" content="light">
|
|
||||||
<title>Ultrawidify - Content Script User Interface (in-player overlay)</title>
|
|
||||||
<!-- <link rel="stylesheet" href="csui.css"> -->
|
|
||||||
</head>
|
|
||||||
<body class="uw-ultrawidify-container-root" style="background-color: transparent">
|
|
||||||
<div id="app"></div>
|
|
||||||
<script src="csui.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Ultrawidify - Content Script User Interface (in-player overlay)</title>
|
|
||||||
<!-- <link rel="stylesheet" href="csui.css"> -->
|
|
||||||
</head>
|
|
||||||
<body class="uw-ultrawidify-container-root" style="background-color: transparent">
|
|
||||||
<div id="app"></div>
|
|
||||||
<script src="csui.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Title</title>
|
|
||||||
<!-- <link rel="stylesheet" href="popup.css"> -->
|
|
||||||
|
|
||||||
<% if (NODE_ENV === 'development') { %>
|
|
||||||
<!-- Load some resources only in development environment -->
|
|
||||||
<% } %>
|
|
||||||
</head>
|
|
||||||
<body
|
|
||||||
style="width: 100%; height: 100%; overflow-x: hidden; min-width: 750px"
|
|
||||||
>
|
|
||||||
<div id="app">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script src="csui-popup.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { createApp } from 'vue'
|
|
||||||
import Popup from './Popup';
|
|
||||||
import mdiVue from 'mdi-vue/v3';
|
|
||||||
import * as mdijs from '@mdi/js';
|
|
||||||
|
|
||||||
createApp(Popup)
|
|
||||||
.use(mdiVue, {icons: mdijs})
|
|
||||||
.mount('#app');
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import PlayerOverlay from './PlayerOverlay';
|
|
||||||
import mdiVue from 'mdi-vue/v3';
|
|
||||||
import * as mdijs from '@mdi/js';
|
|
||||||
|
|
||||||
// NOTE — this is in-player interface for ultrawidify
|
|
||||||
// it is injected into the page in UI.init()
|
|
||||||
|
|
||||||
createApp(PlayerOverlay)
|
|
||||||
.use(mdiVue, {icons: mdijs})
|
|
||||||
.mount('#app');
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
$text-normal: #ddd;
|
|
||||||
$text-dim: #999;
|
|
||||||
$text-dark: #666;
|
|
||||||
$primary-color: #fb772a;
|
|
||||||
$primary: $primary-color;
|
|
||||||
$secondary-color: #e70c0c;
|
|
||||||
|
|
||||||
$input-background: #141414;
|
|
||||||
$input-border: #4e3527;
|
|
||||||
$page-background: #101010;
|
|
||||||
$popup-header-background: #7f1416;
|
|
||||||
|
|
||||||
$background-primary: #101010;
|
|
||||||
$selected-color: #f5cbaf;
|
|
||||||
$background-selected: #412d20;
|
|
||||||
|
|
||||||
|
|
||||||
$info-color: #bda9f3;
|
|
||||||
@ -1,396 +0,0 @@
|
|||||||
@import "colors.scss";
|
|
||||||
@import "fonts.scss";
|
|
||||||
@import "flex.scss";
|
|
||||||
// @import "~/@mdi/font/css/materialdesignicons.css";
|
|
||||||
|
|
||||||
// @import "form.scss";
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $background-primary;
|
|
||||||
color: $text-normal;
|
|
||||||
font-family: 'Overpass', sans-serif;
|
|
||||||
font-size: 1.2em;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
border: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STANDARD WIDTHS AND HEIGHTS */
|
|
||||||
.w100 {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h100, .h-full {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w24 {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-t-0-33em {
|
|
||||||
margin-top: 0.33em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.x-pad-1em {
|
|
||||||
padding-left: 1em;
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* overflow stuff */
|
|
||||||
.overflow-y-auto {
|
|
||||||
overflow: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: rgba($primary-color, 0.7) $background-primary;
|
|
||||||
}
|
|
||||||
.scrollbar-darker {
|
|
||||||
scrollbar-color: rgba($primary-color, 0.5) $background-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* scrollbars for chrome/webkit */
|
|
||||||
.overflow-y-auto::-webkit-scrollbar {
|
|
||||||
width: 3px;
|
|
||||||
}
|
|
||||||
.overflow-y-auto::-webkit-scrollbar-track {
|
|
||||||
background: $background-primary;
|
|
||||||
}
|
|
||||||
.overflow-y-auto::-webkit-scrollbar-thumb {
|
|
||||||
background: rgba($primary-color, 0.7);
|
|
||||||
}
|
|
||||||
.overflow-y-auto::-webkit-scrollbar-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.scrollbar-darker::-webkit-scrollbar-thumb {
|
|
||||||
background: rgba($primary-color, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-overflow {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-overflow-x {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-overflow-y {
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .SELECTED CLASSES */
|
|
||||||
|
|
||||||
.selected-tab {
|
|
||||||
background-color: initial;
|
|
||||||
border-left: $primary-color 5px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-tab-secondary {
|
|
||||||
background-color: initial;
|
|
||||||
border-left: $secondary-color 3px solid !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* BASIC STYLING */
|
|
||||||
|
|
||||||
.description {
|
|
||||||
color: $text-dim;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding-top: 1.5rem;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-variant: small-caps;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-secondary {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-box {
|
|
||||||
padding-top: 0.69rem;
|
|
||||||
padding-bottom: 0.69rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indent {
|
|
||||||
padding-left: 4.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-padding {
|
|
||||||
padding-top: 0.69rem;
|
|
||||||
padding-bottom: 0.69rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: lighten($primary-color, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* INPUT FORMATTING */
|
|
||||||
input[type="number"], input[type="text"], input {
|
|
||||||
outline: none;
|
|
||||||
background-color: $input-background;
|
|
||||||
color: $text-normal;
|
|
||||||
padding: 0.1rem;
|
|
||||||
padding-top: 0.2rem;
|
|
||||||
padding-bottom: 0.1rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
border: 1px solid $input-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:disabled {
|
|
||||||
background: #444444;
|
|
||||||
color: darken($text-normal, 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ELEMENT POSITIONING */
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: block;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-row {
|
|
||||||
display: block;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float-left {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.float-right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inline-block {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* TEXT FORMATTING (no colors) */
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 0.75em;
|
|
||||||
font-weight: 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small {
|
|
||||||
font-size: 0.75em;
|
|
||||||
font-weight: 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.medium-small {
|
|
||||||
font-size: 0.85em;
|
|
||||||
font-weight: 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smallcaps{
|
|
||||||
font-variant: small-caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center{
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.text-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.invalid-input {
|
|
||||||
border: 1px solid #720 !important;
|
|
||||||
background-color: #410 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
/*display: inline-block;*/
|
|
||||||
// padding-top: 8px;
|
|
||||||
// padding-bottom: 3px;
|
|
||||||
//padding-left: 5px;
|
|
||||||
//padding-right: 5px;
|
|
||||||
border: 1px solid rgb(39, 39, 39);
|
|
||||||
margin-top: 3px;
|
|
||||||
margin-bottom: 3px;
|
|
||||||
color: $text-dim;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.selected, .setting-selected {
|
|
||||||
color: $selected-color !important;
|
|
||||||
background-color: $background-selected !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-tab {
|
|
||||||
color: $selected-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-selected {
|
|
||||||
border: 1px solid shade($selected-color, 25%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
color: lighten($selected-color, 10%);
|
|
||||||
background-color: lighten($background-selected, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
/* color: #666; */
|
|
||||||
filter: contrast(50%) brightness(40%) grayscale(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled-button {
|
|
||||||
color: #666 !important;
|
|
||||||
cursor: not-allowed !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled-button:hover {
|
|
||||||
color: #777 !important;
|
|
||||||
background-color: #222 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BROWSER-SPECIFIC DISABLE */
|
|
||||||
.disabled-edge {
|
|
||||||
pointer-events: none !important;
|
|
||||||
filter: contrast(50%) brightness(40%) grayscale(100%) !important;
|
|
||||||
content: "NOT SUPPORTED IN THIS BROWSER";
|
|
||||||
}
|
|
||||||
.disabled-edge::after {
|
|
||||||
background-color: #333272;
|
|
||||||
color: #d8d9e6;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: .75em;
|
|
||||||
font-variant: small-caps;
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 5px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** misc **/
|
|
||||||
|
|
||||||
.warning-color {
|
|
||||||
color: #d6ba4a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning, .warning-lite {
|
|
||||||
color: #d6ba4a;
|
|
||||||
padding-left: 35px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning::before, .warning-lite::before {
|
|
||||||
content: "⚠ ";
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.warning::before {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 2.5em;
|
|
||||||
margin-left: -35px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
color: $info-color;
|
|
||||||
padding-left: 35px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info::before {
|
|
||||||
content: "ⓘ";
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-left: -35px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new::after {
|
|
||||||
content: "ⓘ";
|
|
||||||
color: $info-color !important;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.experimental::after {
|
|
||||||
// content: "Experimental";
|
|
||||||
content: "Ⓔ";
|
|
||||||
color: #ffde12;
|
|
||||||
// background-color: #1f1f1f;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: .75em;
|
|
||||||
font-variant: small-caps;
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 5px;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
.experimental:hover::after {
|
|
||||||
content: "Ⓔ Experimental";
|
|
||||||
// content: "";
|
|
||||||
color: #ffde12;
|
|
||||||
// background-color: #1f1f1f;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: .75em;
|
|
||||||
font-variant: small-caps;
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 5px;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sticky-bottom {
|
|
||||||
width: 100%;
|
|
||||||
position: sticky;
|
|
||||||
// position: fixed;
|
|
||||||
bottom: 0px;
|
|
||||||
background-color: rgba($background-primary, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rtl {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ltr {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
.monospace {
|
|
||||||
font-family: 'Overpass Mono';
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-col {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-auto {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-grow {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
.flex-nogrow {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
.flex-shrink {
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
.flex-noshrink {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.flex-wrap {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-center {
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-end {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-cross-center {
|
|
||||||
justify-content: center;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-self-center {
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-thin.woff2') format('woff2'); /* Super Modern Browsers */
|
|
||||||
font-weight: 200;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-thin-italic.woff2') format('woff2');
|
|
||||||
font-weight: 200;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-extralight.woff2') format('woff2');
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-extralight-italic.woff2') format('woff2');
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-light.woff2') format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-light-italic.woff2') format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-regular.woff2') format('woff2');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-italic.woff2') format('woff2');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-semibold.woff2') format('woff2');
|
|
||||||
font-weight: 600;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-semibold-italic.woff2') format('woff2');
|
|
||||||
font-weight: 600;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'overpass';
|
|
||||||
src: url('../../fonts/overpass-webfont/overpass-bold-italic.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-thin.woff2') format('woff2'); /* Super Modern Browsers */
|
|
||||||
font-weight: 200;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-thin-italic.woff2') format('woff2');
|
|
||||||
font-weight: 200;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-extralight.woff2') format('woff2');
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-extralight-italic.woff2') format('woff2');
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-light.woff2') format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-light-italic.woff2') format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-regular.woff2') format('woff2');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-italic.woff2') format('woff2');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-semibold.woff2') format('woff2');
|
|
||||||
font-weight: 600;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-semibold-italic.woff2') format('woff2');
|
|
||||||
font-weight: 600;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
src: url('/csui/res/fonts/overpass-webfont/overpass-bold-italic.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass mono';
|
|
||||||
src: url('/csui/res/fonts/overpass-mono-webfont/overpass-mono-light.woff2') format('woff2');
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass mono';
|
|
||||||
src: url('/csui/res/fonts/overpass-mono-webfont/overpass-mono-regular.woff2') format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass mono';
|
|
||||||
src: url('/csui/res/fonts/overpass-mono-webfont/overpass-mono-semibold.woff2') format('woff2');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Overpass mono';
|
|
||||||
src: url('/csui/res/fonts/overpass-mono-webfont/overpass-mono-bold.woff2') format('woff2');
|
|
||||||
font-weight: 600;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
@import 'flex.scss';
|
|
||||||
@import 'colors.scss';
|
|
||||||
@import 'common.scss';
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: 'Overpass';
|
|
||||||
}
|
|
||||||
|
|
||||||
.uw-ultrawidify-container-root {
|
|
||||||
// here's the defaults:
|
|
||||||
// all: initial;
|
|
||||||
// * {
|
|
||||||
// all: unset;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// here's things that we don't want as defaults
|
|
||||||
// (must come after the all: declaration, otherwise
|
|
||||||
// all: declaration overrides everything.)
|
|
||||||
|
|
||||||
// we put our UI _over_ site's player:
|
|
||||||
z-index: 999999;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-color: none transparent !important;
|
|
||||||
|
|
||||||
// Ensure we're display:block
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
// we are click-through by default:
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
## License
|
|
||||||
|
|
||||||
Copyright 2015 Red Hat, Inc.,
|
|
||||||
This Font Software is dual licensed and available under the SIL Open Font License, Version 1.1. and also the LGPL 2.1
|
|
||||||
|
|
||||||
The Open Font license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
The GNU LGPL can be found at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### SIL OPEN FONT LICENSE
|
|
||||||
Version 1.1 - 26 February 2007
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
#### DEFINITIONS
|
|
||||||
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
|
|
||||||
|
|
||||||
“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
|
||||||
|
|
||||||
“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
#### PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
|
||||||
|
|
||||||
#### TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are not met.
|
|
||||||
|
|
||||||
#### DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
@ -1,497 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="popup-panel flex flex-col uw-clickable h-full"
|
|
||||||
>
|
|
||||||
<div class="popup-window-header">
|
|
||||||
<div class="header-title">
|
|
||||||
<div class="popup-title">Ultrawidify <small>{{settings?.active?.version}} - {{BrowserDetect.processEnvChannel}}</small></div>
|
|
||||||
<div class="site-support-info">
|
|
||||||
<div class="site-support-site">{{site}}</div>
|
|
||||||
<template v-if="inPlayer">
|
|
||||||
<div v-if="siteSupportLevel === 'official'" class="site-support official">
|
|
||||||
<mdicon name="check-decagram" />
|
|
||||||
<div>Verified</div>
|
|
||||||
<div class="tooltip">The extension is being tested and should work on this site.</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="siteSupportLevel === 'community'" class="site-support community">
|
|
||||||
<mdicon name="handshake" />
|
|
||||||
<div>Community</div>
|
|
||||||
<div class="tooltip">
|
|
||||||
People say extension works on this site (or have provided help getting the extension to work if it didn't).<br/><br/>
|
|
||||||
Tamius (the dev) does not test the extension on this site, probably because it requires a subscription or
|
|
||||||
is geoblocked.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="siteSupportLevel === 'no-support'" class="site-support no-support">
|
|
||||||
<mdicon name="help-circle-outline" />
|
|
||||||
<div>Unknown</div>
|
|
||||||
<div class="tooltip">
|
|
||||||
Not officially supported. Extension will try to fix things, but no promises.<br/><br/>
|
|
||||||
Tamius (the dev) does not test the extension on this site for various reasons
|
|
||||||
(unaware, not using the site, language barrier, geoblocking, paid services Tam doesn't use).
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="siteSupportLevel === 'user-added'" class="site-support user-added">
|
|
||||||
<mdicon name="account" />
|
|
||||||
<div>Custom</div>
|
|
||||||
<div class="tooltip">
|
|
||||||
You have manually changed settings for this site. The extension is doing what you told it to do.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<mdicon v-if="siteSupportLevel === 'community'" class="site-support supported" name="checkbox-marked-circle" />
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="header-buttons">
|
|
||||||
<div
|
|
||||||
class="header-button"
|
|
||||||
:class="{'button-active': preventClose}"
|
|
||||||
@click="setPreventClose(!preventClose)"
|
|
||||||
>
|
|
||||||
<mdicon v-if="!preventClose" name="pin-outline" :size="32" />
|
|
||||||
<mdicon v-else name="pin" :size="32" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="header-button close-button"
|
|
||||||
@click="$emit('close')"
|
|
||||||
>
|
|
||||||
<mdicon name="close" :size="36"></mdicon>
|
|
||||||
</div>
|
|
||||||
<!-- <a >{{preventClose ? 'allow auto-close' : 'prevent auto-close'}}</a> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-main flex flex-row">
|
|
||||||
<div class="tab-row flex flex-col grow-0 shrink-0">
|
|
||||||
<div
|
|
||||||
v-for="tab of tabs"
|
|
||||||
:key="tab.id"
|
|
||||||
class="tab"
|
|
||||||
:class="{
|
|
||||||
'active': tab.id === selectedTab,
|
|
||||||
'highlight-tab': tab.highlight,
|
|
||||||
}"
|
|
||||||
@click="selectTab(tab.id)"
|
|
||||||
>
|
|
||||||
<div class="icon-container">
|
|
||||||
<mdicon
|
|
||||||
v-if="tab.icon"
|
|
||||||
:name="tab.icon"
|
|
||||||
:size="32"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="label">
|
|
||||||
{{tab.label}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content flex flex-col">
|
|
||||||
<!-- autodetection warning -->
|
|
||||||
|
|
||||||
<div class="warning-area">
|
|
||||||
<div
|
|
||||||
v-if="statusFlags.hasDrm"
|
|
||||||
class="warning-box"
|
|
||||||
>
|
|
||||||
<div class="icon-container">
|
|
||||||
<mdicon name="alert" :size="32" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
This site is blocking automatic aspect ratio detection. You will have to adjust aspect ratio manually.<br/>
|
|
||||||
<a>Learn more ...</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row panel-content">
|
|
||||||
<!-- Panel section -->
|
|
||||||
<VideoSettings
|
|
||||||
v-if="selectedTab === 'videoSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:site="site"
|
|
||||||
></VideoSettings>
|
|
||||||
<PlayerDetectionPanel
|
|
||||||
v-if="selectedTab === 'playerDetection'"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:site="site"
|
|
||||||
>
|
|
||||||
</PlayerDetectionPanel>
|
|
||||||
<BaseExtensionSettings
|
|
||||||
v-if="selectedTab === 'extensionSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:site="site"
|
|
||||||
></BaseExtensionSettings>
|
|
||||||
<AutodetectionSettingsPanel
|
|
||||||
v-if="selectedTab === 'autodetectionSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:site="site"
|
|
||||||
>
|
|
||||||
</AutodetectionSettingsPanel>
|
|
||||||
<DebugPanel
|
|
||||||
v-if="selectedTab === 'debugging'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:site="site"
|
|
||||||
></DebugPanel>
|
|
||||||
<ChangelogPanel
|
|
||||||
v-if="selectedTab === 'changelog'"
|
|
||||||
:settings="settings"
|
|
||||||
></ChangelogPanel>
|
|
||||||
<AboutPanel
|
|
||||||
v-if="selectedTab === 'about'"
|
|
||||||
>
|
|
||||||
</AboutPanel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import DebugPanel from './PlayerUiPanels/DebugPanel.vue'
|
|
||||||
import AutodetectionSettingsPanel from './PlayerUiPanels/AutodetectionSettingsPanel.vue'
|
|
||||||
import BaseExtensionSettings from './PlayerUiPanels/BaseExtensionSettings.vue'
|
|
||||||
import PlayerDetectionPanel from './PlayerUiPanels/PlayerDetectionPanel.vue'
|
|
||||||
import VideoSettings from './PlayerUiPanels/VideoSettings.vue'
|
|
||||||
import BrowserDetect from '../../ext/conf/BrowserDetect'
|
|
||||||
import ChangelogPanel from './PlayerUiPanels/ChangelogPanel.vue'
|
|
||||||
import AboutPanel from './PlayerUiPanels/AboutPanel.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
VideoSettings,
|
|
||||||
PlayerDetectionPanel,
|
|
||||||
BaseExtensionSettings,
|
|
||||||
AutodetectionSettingsPanel,
|
|
||||||
DebugPanel,
|
|
||||||
ChangelogPanel,
|
|
||||||
AboutPanel
|
|
||||||
},
|
|
||||||
mixins: [],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
statusFlags: {
|
|
||||||
hasDrm: undefined,
|
|
||||||
},
|
|
||||||
|
|
||||||
tabs: [
|
|
||||||
{id: 'videoSettings', label: 'Video settings', icon: 'crop'},
|
|
||||||
{id: 'playerDetection', label: 'Player detection', icon: 'television-play'},
|
|
||||||
{id: 'extensionSettings', label: 'Site and Extension options', icon: 'cogs' },
|
|
||||||
// {id: 'autodetectionSettings', label: 'Autodetection options', icon: ''},
|
|
||||||
// {id: 'advancedOptions', label: 'Advanced options', icon: 'cogs' },
|
|
||||||
// {id: 'debugging', label: 'Debugging', icon: 'bug-outline' }
|
|
||||||
{id: 'changelog', label: 'What\'s new', icon: 'information-box-outline' },
|
|
||||||
{id: 'about', label: 'About', icon: 'star-four-points-circle'}
|
|
||||||
],
|
|
||||||
selectedTab: 'videoSettings',
|
|
||||||
BrowserDetect: BrowserDetect,
|
|
||||||
preventClose: false,
|
|
||||||
siteSettings: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'eventBus',
|
|
||||||
'logger',
|
|
||||||
'in-player',
|
|
||||||
'site'
|
|
||||||
],
|
|
||||||
computed: {
|
|
||||||
// LPT: NO ARROW FUNCTIONS IN COMPUTED,
|
|
||||||
// IS SUPER HARAM
|
|
||||||
// THINGS WILL NOT WORK IF YOU USE ARROWS
|
|
||||||
siteSupportLevel() {
|
|
||||||
return (this.site && this.siteSettings) ? this.siteSettings.data.type || 'no-support' : 'waiting';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.siteSettings = this.settings.getSiteSettings(this.site);
|
|
||||||
this.tabs.find(x => x.id === 'changelog').highlight = !this.settings.active.whatsNewChecked;
|
|
||||||
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'uw-show-ui',
|
|
||||||
() => {
|
|
||||||
if (this.inPlayer) {
|
|
||||||
return; // show-ui is only intended for global overlay
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Gets URL of the browser settings page (i think?)
|
|
||||||
*/
|
|
||||||
getUrl(url) {
|
|
||||||
return BrowserDetect.getURL(url);
|
|
||||||
},
|
|
||||||
selectTab(tab) {
|
|
||||||
this.selectedTab = tab;
|
|
||||||
},
|
|
||||||
setPreventClose(bool) {
|
|
||||||
this.preventClose = bool;
|
|
||||||
this.$emit('preventClose', bool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../res/css/uwui-base.scss';
|
|
||||||
@import '../res/css/colors.scss';
|
|
||||||
@import '../res/css/font/overpass.css';
|
|
||||||
@import '../res/css/font/overpass-mono.css';
|
|
||||||
@import '../res/css/common.scss';
|
|
||||||
@import '../src/res-common/_variables';
|
|
||||||
|
|
||||||
// .relative-wrapper {
|
|
||||||
// position: relative;
|
|
||||||
// width: 100%;
|
|
||||||
// height: 100%;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.tab-row {
|
|
||||||
width: 22rem;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-support-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.site-support-site {
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-support {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
margin-left: 1rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0rem 1.5rem 0rem 1rem;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
padding: 1rem;
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
transform: translateY(110%);
|
|
||||||
width: 42em;
|
|
||||||
|
|
||||||
background-color: rgba(0,0,0,0.90);
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.tooltip {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.official {
|
|
||||||
background-color: #fa6;
|
|
||||||
color: #000;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #000 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.community {
|
|
||||||
background-color: rgb(85, 85, 179);
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #fff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-support {
|
|
||||||
background-color: rgb(138, 65, 126);
|
|
||||||
color: #eee;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #eee !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.user-added {
|
|
||||||
border: 1px solid #ff0;
|
|
||||||
|
|
||||||
color: #ff0;
|
|
||||||
|
|
||||||
.mdi {
|
|
||||||
fill: #ff0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex-grow: 1;
|
|
||||||
|
|
||||||
.warning-area {
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-content {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-box {
|
|
||||||
background: rgb(255, 174, 107);
|
|
||||||
color: #000;
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-container {
|
|
||||||
margin-right: 1rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: rgba(0,0,0,0.7);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.popup-panel {
|
|
||||||
background-color: rgba(0,0,0,0.50);
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.popup-window-header {
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgba(5,5,5, 0.75);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.header-title {
|
|
||||||
flex: 1 1;
|
|
||||||
}
|
|
||||||
.header-buttons {
|
|
||||||
flex: 0 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.header-button {
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.button-active {
|
|
||||||
background-color: #fa6;
|
|
||||||
color: #000;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.close-button {
|
|
||||||
color: #f00;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tab-row {
|
|
||||||
background-color: rgba(11,11,11, 0.75);
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
padding: 2rem;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
height: 4rem;
|
|
||||||
|
|
||||||
border-bottom: 1px solid rgba(128, 128, 128, 0.5);
|
|
||||||
border-top: 1px solid rgba(128, 128, 128, 0.5);
|
|
||||||
opacity: 0.5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
&.active {
|
|
||||||
opacity: 1.0;
|
|
||||||
background-color: $primaryBg;
|
|
||||||
color: rgb(255, 174, 107);
|
|
||||||
border-bottom: 1px solid rgba(116, 78, 47, 0.5);
|
|
||||||
border-top: 1px solid rgba(116, 78, 47, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-container {
|
|
||||||
width: 64px;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.highlight-tab {
|
|
||||||
opacity: 0.9;
|
|
||||||
color: #eee;
|
|
||||||
|
|
||||||
// .label {
|
|
||||||
// color: rgb(239, 192, 152);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-title, .popup-title h1 {
|
|
||||||
font-size: 48px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,140 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col w-full h-full gap-2">
|
|
||||||
<h1>About Ultrawidify</h1>
|
|
||||||
<p>
|
|
||||||
Ultrawidify version: <b>{{addonVersion}}</b><br>
|
|
||||||
Install source: <b>{{addonSource}}</b>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="flex flex-row gap-2 bg-black">
|
|
||||||
<div class="w-[1/2]" style="width: 50%">
|
|
||||||
<h2>Report a problem</h2>
|
|
||||||
<p>
|
|
||||||
You may report <strike>undocumented features</strike> bugs using one of the following options (in order of preference):
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li> <a target="_blank" href="https://github.com/tamius-han/ultrawidify/issues"><b>Github (preferred)</b></a><br/></li>
|
|
||||||
<li>Email: <a target="_blank" :href="mailtoLink">tamius.han@gmail.com</a></li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
When reporting bugs, please include the following information:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>Extension version</li>
|
|
||||||
<li>Which browser you're using¹</li>
|
|
||||||
<li>Which extension store you installed extension from (install source)²</li>
|
|
||||||
<li>Which site you're having the issue on (preferably link. For issues with autodetection, please also include a timestamp)</li>
|
|
||||||
<li>What do I need to do in order to make the issue happen</li>
|
|
||||||
<li>Please include a screenshot of the problem. It usually helps.</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
<small>
|
|
||||||
[1] If using anything other than Firefox, Chrome, or Edge, please check if issue also happens in Chrome or Edge. Bugs affecting Opera will only get fixed if they also affect Chrome, because <a target="_blank" href="https://stuff.tamius.net/sacred-texts/2024/06/08/why-im-boycotting-opera/">I'm salty</a>.<br/>
|
|
||||||
[2] It is recommended that users of Microsoft Edge install Ultrawidify from the Microsoft Edge Addons source
|
|
||||||
</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w-[1/2]" style="width: 50%">
|
|
||||||
<h2>Thank you monies</h2>
|
|
||||||
<p>
|
|
||||||
If you think I deserve money for the work I did up to this point, you can bankroll my caffeine addiction.
|
|
||||||
</p>
|
|
||||||
<p class="text-center">
|
|
||||||
<a class="donate" href="https://www.paypal.com/paypalme/tamius" target="_blank">Donate on Paypal</a>
|
|
||||||
</p>
|
|
||||||
<h2>Fun stuff</h2>
|
|
||||||
<p>
|
|
||||||
This is probably a bad idea but—
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Are you attending Isle of Wonders on Cres, Croatia, between 28. 6. and 30. 6.? So am I, by official duty.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Club Amulet D20 is forecasted to have a stand there, and I am forecasted to be in the general vicinity of it (barring any unexpected circumstances). I'll be either taking photos, painting minis, or doing heatstroke any% in rather rudamentary costume.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
If you're there, you can swing around to say 'hi' or provide some validation, or paint some minis. Rumor has it Conquest will have paint&take event.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
— Tamius
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<small>I am not paid to shill this.</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import BrowserDetect from '../../../ext/conf/BrowserDetect';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
BrowserDetect,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// reminder — webextension-polyfill doesn't seem to work in vue!
|
|
||||||
addonVersion: BrowserDetect.firefox ? chrome.runtime.getManifest().version : chrome.runtime.getManifest().version,
|
|
||||||
addonSource: BrowserDetect.processEnvVersion,
|
|
||||||
mailtoLink: '',
|
|
||||||
redditLink: '',
|
|
||||||
showEasterEgg: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
const messageTemplate = encodeURIComponent(
|
|
||||||
`Describe your issue in more detail. In case of misaligned videos, please provide screenshots. When reporting\
|
|
||||||
issues with autodetection not detecting aspect ratio correctly, please provide a link with timestamp to the\
|
|
||||||
problematic video at the time where the problem happens. You may delete this paragraph, as it's only a template.
|
|
||||||
|
|
||||||
|
|
||||||
Extension info (AUTOGENERATED — DO NOT CHANGE OR REMOVE):
|
|
||||||
* Build: ${BrowserDetect.processEnvBrowser}-${this.addonVersion}-stable
|
|
||||||
|
|
||||||
Browser-related stuff (please ensure this section is correct):
|
|
||||||
* User Agent string: ${window.navigator.userAgent}
|
|
||||||
* vendor: ${window.navigator.vendor}
|
|
||||||
* Operating system: ${window.navigator.platform}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
this.mailtoLink = `mailto:tamius.han@gmail.com?subject=%5BUltrawidify%5D%20ENTER%20SUMMARY%20OF%20YOUR%20ISSUE%20HERE&body=${messageTemplate}`;
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
p, li {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
small {
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
.text-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.donate {
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
background-color: #fa6;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,185 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="alignment-box">
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col top left"
|
|
||||||
@click="align(VideoAlignment.Left, VideoAlignment.Top)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col top center"
|
|
||||||
@click="align(VideoAlignment.Center, VideoAlignment.Top)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col top right"
|
|
||||||
@click="align(VideoAlignment.Right, VideoAlignment.Top)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col ycenter left"
|
|
||||||
@click="align(VideoAlignment.Left, VideoAlignment.Center)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col ycenter center"
|
|
||||||
@click="align(VideoAlignment.Center, VideoAlignment.Center)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col ycenter right"
|
|
||||||
@click="align(VideoAlignment.Right, VideoAlignment.Center)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col bottom left"
|
|
||||||
@click="align(VideoAlignment.Left, VideoAlignment.Bottom)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col bottom center"
|
|
||||||
@click="align(VideoAlignment.Center, VideoAlignment.Bottom)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col bottom right"
|
|
||||||
@click="align(VideoAlignment.Right, VideoAlignment.Bottom)"
|
|
||||||
>
|
|
||||||
<div class="alignment-ui"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: [
|
|
||||||
'eventBus'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
VideoAlignment: VideoAlignmentType
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
align(alignmentX, alignmentY) {
|
|
||||||
this.eventBus?.sendToTunnel('set-alignment', {x: alignmentX, y: alignmentY})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped module>
|
|
||||||
.alignment-box {
|
|
||||||
aspect-ratio: 1;
|
|
||||||
width: 100%;
|
|
||||||
min-width: 40px;
|
|
||||||
max-width: 80px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.row {
|
|
||||||
flex: 0 0 33%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col {
|
|
||||||
flex: 0 0 33%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
background-color: rgba(0,0,0,0.25);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
margin: 0.125rem;
|
|
||||||
padding: 0.5rem;
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
|
|
||||||
border-bottom: 1px solid transparent;
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
|
|
||||||
width: 50%;
|
|
||||||
height: 50%;
|
|
||||||
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0px;
|
|
||||||
border-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(0,0,0,0.5);
|
|
||||||
border-bottom: 1px solid #fa6;
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
border-color: #fa6 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.center.ycenter {
|
|
||||||
.alignment-ui {
|
|
||||||
background-color: #fa6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top {
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
border-top-width: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.bottom {
|
|
||||||
align-items: flex-end;
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
border-bottom-width: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.left {
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
border-left-width: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.right {
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
border-right-width: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.center.ycenter {
|
|
||||||
|
|
||||||
.alignment-ui {
|
|
||||||
width: 25%;
|
|
||||||
height: 25%;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,478 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col tab-root">
|
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
|
|
||||||
<!-- AARD performance metrics -->
|
|
||||||
<div class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h1><mdicon name="television-play" :size="32" /> Automatic Aspect Ratio Detection</h1>
|
|
||||||
</div>
|
|
||||||
<div class="sub-panel-content">
|
|
||||||
<p>
|
|
||||||
<b>Autodetection performance</b>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Automatic aspect ratio detection is a resource-hungry feature.
|
|
||||||
This page allows you to trade autodetection accuracy and/or frequency for
|
|
||||||
better performance.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Note that some browsers <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/now" target="_blank">limit the accuracy of time measurements</a>, though once the bars go past the blue line those limitations are largely inconsequential.
|
|
||||||
</p>
|
|
||||||
<div class="performance-graph-container">
|
|
||||||
<div class="performance-graph">
|
|
||||||
<div class="time-budget hz144"></div>
|
|
||||||
<div class="time-budget hz120"></div>
|
|
||||||
<div class="time-budget hz60"></div>
|
|
||||||
<div class="time-budget hz30"></div>
|
|
||||||
<div class="time-budget hz24"></div>
|
|
||||||
<div class="time-budget rest"></div>
|
|
||||||
|
|
||||||
<div class="bar-container">
|
|
||||||
<div class="average-case">
|
|
||||||
<div class="stats">
|
|
||||||
<b>Average: </b>
|
|
||||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="processing">
|
|
||||||
processing {{
|
|
||||||
Math.max(
|
|
||||||
(performanceData?.total?.averageTime ?? 0)
|
|
||||||
- (performanceData?.imageDraw?.averageTime ?? 0)
|
|
||||||
- (performanceData?.blackFrame?.averageTime ?? 0),
|
|
||||||
0
|
|
||||||
).toFixed(1)
|
|
||||||
}} ms
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="bar">
|
|
||||||
<div
|
|
||||||
class="draw"
|
|
||||||
:style="{'width': (performanceData?.imageDraw?.averageTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="draw-blackframe"
|
|
||||||
:style="{'width': (performanceData?.blackFrame?.averageTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="processing"
|
|
||||||
:style="{
|
|
||||||
'width': Math.max(
|
|
||||||
(performanceData?.total?.averageTime ?? 0)
|
|
||||||
- (performanceData?.imageDraw?.averageTime ?? 0)
|
|
||||||
- (performanceData?.blackFrame?.averageTime ?? 0),
|
|
||||||
0
|
|
||||||
) + '%'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="worst-case">
|
|
||||||
<div class="stats">
|
|
||||||
<b>Worst: </b>
|
|
||||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="processing">
|
|
||||||
processing {{
|
|
||||||
Math.max(
|
|
||||||
(performanceData?.total?.worstTime ?? 0)
|
|
||||||
- (performanceData?.imageDraw?.worstTime ?? 0)
|
|
||||||
- (performanceData?.blackFrame?.worstTime ?? 0),
|
|
||||||
0
|
|
||||||
).toFixed(1)
|
|
||||||
}} ms
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="bar">
|
|
||||||
<div
|
|
||||||
class="draw"
|
|
||||||
:style="{'width': (performanceData?.imageDraw?.worstTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="draw-blackframe"
|
|
||||||
:style="{'width': (performanceData?.blackFrame?.worstTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="processing"
|
|
||||||
:style="{
|
|
||||||
'width': Math.max(
|
|
||||||
(performanceData?.total?.worstTime ?? 0)
|
|
||||||
- (performanceData?.imageDraw?.worstTime ?? 0)
|
|
||||||
- (performanceData?.blackFrame?.worstTime ?? 0),
|
|
||||||
0
|
|
||||||
) + '%'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="average-case">
|
|
||||||
<div class="stats">
|
|
||||||
<b>AR change (average): </b>
|
|
||||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="processing">processing {{
|
|
||||||
(
|
|
||||||
(performanceData?.fastLetterbox?.averageTime ?? 0)
|
|
||||||
+ (performanceData?.edgeDetect?.averageTime ?? 0)
|
|
||||||
).toFixed(1)
|
|
||||||
}} ms</span>
|
|
||||||
</div>
|
|
||||||
<div class="bar">
|
|
||||||
<div
|
|
||||||
class="draw"
|
|
||||||
:style="{'width': (performanceData?.imageDraw?.averageTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="draw-blackframe"
|
|
||||||
:style="{'width': (performanceData?.blackFrame?.averageTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="processing"
|
|
||||||
:style="{
|
|
||||||
'width': (
|
|
||||||
(performanceData?.fastLetterbox?.averageTime ?? 0)
|
|
||||||
+ (performanceData?.edgeDetect?.averageTime ?? 0)
|
|
||||||
) + '%'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="worst-case">
|
|
||||||
<div class="stats">
|
|
||||||
<b>AR change (worst): </b>
|
|
||||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
|
||||||
<span class="processing">processing {{
|
|
||||||
(
|
|
||||||
(performanceData?.fastLetterbox?.worstTime ?? 0)
|
|
||||||
+ (performanceData?.edgeDetect?.worstTime ?? 0)
|
|
||||||
).toFixed(1)
|
|
||||||
}} ms</span>
|
|
||||||
</div>
|
|
||||||
<div class="bar">
|
|
||||||
<div
|
|
||||||
class="draw"
|
|
||||||
:style="{'width': (performanceData?.imageDraw?.worstTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="draw-blackframe"
|
|
||||||
:style="{'width': (performanceData?.blackFrame?.worstTime ?? 0) + '%'}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="processing"
|
|
||||||
:style="{
|
|
||||||
'width': (
|
|
||||||
(performanceData?.fastLetterbox?.worstTime ?? 0)
|
|
||||||
+ (performanceData?.edgeDetect?.worstTime ?? 0)
|
|
||||||
) + '%'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-segment">
|
|
||||||
<h2>Basic settings</h2>
|
|
||||||
|
|
||||||
<div class="option">
|
|
||||||
<div class="name">
|
|
||||||
Autodetection frequency
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
Shorter intervals (left side of the slider) are more responsive to changes in aspect ratio detections,
|
|
||||||
but requires more system resources.
|
|
||||||
</div>
|
|
||||||
<div class="indent">
|
|
||||||
<div class="flex flex-row row-padding">
|
|
||||||
<div class="flex flex-input">
|
|
||||||
More often <small>(~60/s)</small>
|
|
||||||
<input type="range"
|
|
||||||
:value="Math.log(settings.active.arDetect.timers.playing)"
|
|
||||||
@change="setArCheckFrequency($event.target.value)"
|
|
||||||
min="2.3"
|
|
||||||
max="9.3"
|
|
||||||
step="any"
|
|
||||||
/>
|
|
||||||
Less often <small>(~1/10s)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="option">
|
|
||||||
<div class="name">
|
|
||||||
Autodetection sensitivity
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Maximum allowed vertical video misalignment:</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="settings.active.arDetect.allowedMisaligned" />
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
Ultrawidify detects letterbox only if video is vertically centered. Some people are bad at vertically
|
|
||||||
centering the content, though. This is how off-center the video can be before autodetection will
|
|
||||||
refuse to crop it.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="option">
|
|
||||||
<div class="name">Video sample size</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="settings.active.arDetect.canvasDimensions.sampleCanvas.width" /> x <input v-model="settings.active.arDetect.canvasDimensions.sampleCanvas.height" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Sample columns:</div>
|
|
||||||
<div class="input"><input v-model="settings.active.arDetect.sampling.staticCols" /></div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Sample rows:</div>
|
|
||||||
<div class="input"><input v-model="settings.active.arDetect.sampling.staticRows" /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Button from '../components/Button.vue'
|
|
||||||
import KeyboardShortcutParser from '../../../common/js/KeyboardShortcutParser';
|
|
||||||
import ShortcutButton from '../components/ShortcutButton';
|
|
||||||
import EditShortcutButton from '../components/EditShortcutButton';
|
|
||||||
import BrowserDetect from '../../../ext/conf/BrowserDetect';
|
|
||||||
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
|
||||||
import StretchType from '../../../common/enums/StretchType.enum';
|
|
||||||
import CropModePersistence from '../../../common/enums/CropModePersistence.enum';
|
|
||||||
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
exec: null,
|
|
||||||
performanceData: {},
|
|
||||||
graphRefreshInterval: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'frame',
|
|
||||||
'eventBus',
|
|
||||||
'site'
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.eventBus.sendToTunnel('get-aard-timing');
|
|
||||||
this.graphRefreshInterval = setInterval(() => this.eventBus.sendToTunnel('get-aard-timing'), 500);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
clearInterval(this.graphRefreshInterval);
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
Button,
|
|
||||||
AlignmentOptionsControlComponent
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async openOptionsPage() {
|
|
||||||
BrowserDetect.runtime.openOptionsPage();
|
|
||||||
},
|
|
||||||
refreshGraph() {
|
|
||||||
this.eventBus.sendToTunnel('get-aard-timing');
|
|
||||||
},
|
|
||||||
handleConfigBroadcast(data) {
|
|
||||||
if (data.type === 'aard-performance-data') {
|
|
||||||
this.performanceData = data.performanceData;
|
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../res-common/common.scss" scoped module></style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped module>
|
|
||||||
@import '../res-common/variables';
|
|
||||||
|
|
||||||
.performance-graph-container {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 8rem;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
.performance-graph {
|
|
||||||
border: 1px solid #fa6;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
// graph is 100 ms wide
|
|
||||||
.time-budget {
|
|
||||||
height: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
&.hz144 {
|
|
||||||
width: 6.9%;
|
|
||||||
// background-color: rgba(143, 143, 235, 0.47);
|
|
||||||
}
|
|
||||||
&.hz120 {
|
|
||||||
width: 1.39%;
|
|
||||||
// background-color: rgba(108, 108, 211, 0.441);
|
|
||||||
}
|
|
||||||
&.hz60 {
|
|
||||||
width: 8.33%;
|
|
||||||
// background-color: rgba(78, 78, 182, 0.327);
|
|
||||||
border-right: 2px solid rgb(96, 96, 227);
|
|
||||||
&::after {
|
|
||||||
content: '60fps';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: -2px;
|
|
||||||
|
|
||||||
border-right: 2px solid rgb(96, 96, 227);
|
|
||||||
|
|
||||||
transform: translateY(-100%);
|
|
||||||
|
|
||||||
font-size: 0.6rem;
|
|
||||||
padding-right: 0.25rem;
|
|
||||||
text-transform: small-caps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.hz30 {
|
|
||||||
width: 16.67%;
|
|
||||||
// background-color: rgba(56, 56, 151, 0.308);
|
|
||||||
border-right: 2px solid #fb772a;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '30fps';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: -2px;
|
|
||||||
|
|
||||||
border-right: 2px solid #fb772a;
|
|
||||||
|
|
||||||
transform: translateY(-100%);
|
|
||||||
|
|
||||||
font-size: 0.6rem;
|
|
||||||
padding-right: 0.25rem;
|
|
||||||
text-transform: small-caps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.hz24 {
|
|
||||||
width: 8.33%;
|
|
||||||
// background-color: rgba(37, 37, 118, 0.269);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.bar-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
height: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats {
|
|
||||||
background-color: rgba(0, 0, 0, 0.544);
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-family: 'Overpass Mono';
|
|
||||||
z-index: 11010;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
z-index: 11011;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
width: 0.75rem;
|
|
||||||
height: 0.75rem;
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.draw::before {
|
|
||||||
background-color: #fb772a;
|
|
||||||
}
|
|
||||||
&.draw-blackframe::before {
|
|
||||||
background-color: #e70c0c;
|
|
||||||
}
|
|
||||||
&.processing::before {
|
|
||||||
background-color: rgb(176, 167, 239);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar {
|
|
||||||
width: 100%;
|
|
||||||
height: 0.69rem;
|
|
||||||
background-color: #000;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
* {
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draw {
|
|
||||||
background-color: #fb772a;
|
|
||||||
}
|
|
||||||
.draw-blackframe {
|
|
||||||
background-color: #e70c0c;
|
|
||||||
}
|
|
||||||
.processing {
|
|
||||||
background-color: rgb(176, 167, 239);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col w-100">
|
|
||||||
|
|
||||||
<!-- TAB ROW -->
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div
|
|
||||||
class="tab"
|
|
||||||
:class="{'active': tab === 'siteSettings'}"
|
|
||||||
@click="setTab('siteSettings')"
|
|
||||||
>
|
|
||||||
Settings for current site<br/>
|
|
||||||
<small>{{ site }}</small>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab"
|
|
||||||
:class="{'active': tab === 'extensionSettings'}"
|
|
||||||
@click="setTab(tab = 'extensionSettings')"
|
|
||||||
>
|
|
||||||
Default settings for extension
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab"
|
|
||||||
:class="{'active': tab === 'otherSites'}"
|
|
||||||
@click="setTab(tab = 'otherSites')"
|
|
||||||
>
|
|
||||||
Settings for other sites
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-if="tab === 'siteSettings' && siteSettings">
|
|
||||||
<!-- <div class="button">
|
|
||||||
Reset settings for site
|
|
||||||
</div> -->
|
|
||||||
<SiteExtensionSettings
|
|
||||||
v-if="settings"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:isDefaultConfiguration="false"
|
|
||||||
></SiteExtensionSettings>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="tab === 'extensionSettings' && globalSettings">
|
|
||||||
<SiteExtensionSettings
|
|
||||||
v-if="settings"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="globalSettings"
|
|
||||||
:isDefaultConfiguration="true"
|
|
||||||
></SiteExtensionSettings>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="tab === 'otherSites'">
|
|
||||||
<OtherSiteSettings
|
|
||||||
v-if="settings"
|
|
||||||
:settings="settings"
|
|
||||||
>
|
|
||||||
</OtherSiteSettings>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SiteExtensionSettings from './PanelComponents/ExtensionSettings/SiteExtensionSettings.vue';
|
|
||||||
import OtherSiteSettings from './PanelComponents/ExtensionSettings/OtherSiteSettings.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
tab: 'siteSettings'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'site',
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
SiteExtensionSettings,
|
|
||||||
OtherSiteSettings
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
globalSettings() {
|
|
||||||
return this.settings?.getSiteSettings('@global') ?? null;
|
|
||||||
},
|
|
||||||
siteSettings() {
|
|
||||||
if (this.site) {
|
|
||||||
return this.settings?.getSiteSettings(this.site) ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setTab(tab) {
|
|
||||||
this.tab = tab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../res-common/common.scss" scoped module></style>
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col w-full h-full gap-2">
|
|
||||||
<div class="flex flex-row gap-2 bg-black">
|
|
||||||
<div class="w-[1/2]" style="width: 50%">
|
|
||||||
<h1>What's new</h1>
|
|
||||||
<p>Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md" target="_blank">is available here</a>.</p>
|
|
||||||
|
|
||||||
<h2>6.0.0</h2>
|
|
||||||
<p>
|
|
||||||
I don't think I need to write a changelog for this one. I've also been working on this, on and (mostly) off, for ... a long time, so I might have "kinda forgot" some minor things.
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li><b>Manifest v3</b>. Bit late, but still.</li>
|
|
||||||
<li>
|
|
||||||
<b>In-player UI.</b><br/>
|
|
||||||
This one took major effort to pull off. Required some changes under the hood, required me to de-spaghettify some code. The UI will only show
|
|
||||||
if the video takes up sufficient amount of space (currently determined as 960 pixels wide).
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b>New alignment options.</b> Video can be aligned vertically as well as horizontally.
|
|
||||||
</li>
|
|
||||||
<!-- <li>
|
|
||||||
By default, Ultrawidify will not kick in on "small videos". You have to be either in full screen or theater mode. Ultrawidify assumes that any
|
|
||||||
video that takes up more than 85% of the window width is being viewed in theater mode. This value may be tweaked later on. This feature can be
|
|
||||||
configured (or turned off entirely) via 'Advanced options' menu on the in-player UI.
|
|
||||||
</li> -->
|
|
||||||
<li>
|
|
||||||
The in-player extension UI will do a better job differentiating between the various level of support: "official" for the sites that Tam can check
|
|
||||||
on his own, "fingers crossed" for sites that Tam can't check because my load-bearing credit card can't support the weight of a dozen subscription
|
|
||||||
services, and "community support" for sites that enjoy support through the helping hand of people willing to help.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
The in-player UI should now display a warning whenever Ultrawidify detects automatic aspect ratio detection isn't happening because the site uses DRM.
|
|
||||||
</li>
|
|
||||||
<!-- <li>
|
|
||||||
Better zooming. The slider is back, baby.
|
|
||||||
</li> -->
|
|
||||||
<!-- <li>
|
|
||||||
Panning option that's a bit more intuitive.
|
|
||||||
</li> -->
|
|
||||||
</ul>
|
|
||||||
<h3>Unbaked features</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<b>Player UI settings</b>. Ultrawidify doesn't always correctly identify which area the video should fill, especially when not in full screen mode.
|
|
||||||
Version 6.0.0 was intended to come with UI that makes it easier for you to "help" ultrawidify by identifying the correct player element. Alas, I was
|
|
||||||
busier than expected and manifest v3 deadline came faster than expected.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b>Disabling Ultrawidify UI trigger zone, or otherwise changing target area size and position, is not possible</b>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h3>Regressions</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Due to major changes under the hood, custom settings from older versions were NOT been migrated (probably).
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
It is unclear how extension acts on sites with more than one video.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Extension lost the ability to discriminate between iframes. Actions taken through the popup will be affect <b>all</b> iframes.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Hope the UI is worth the regressions, because getting UI to work has been a MAJOR pain in the ass.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="w-[1/2]" style="width: 50%; padding-left: 1rem; padding-top: 5rem;">
|
|
||||||
<h2>Thank you monies</h2>
|
|
||||||
<p>
|
|
||||||
If you think I deserve money for the work I did up to this point, you can bankroll my caffeine addiction.
|
|
||||||
</p>
|
|
||||||
<p class="text-center">
|
|
||||||
<a class="donate" href="https://www.paypal.com/paypalme/tamius" target="_blank">Donate on Paypal</a>
|
|
||||||
</p>
|
|
||||||
<h2>Fun stuff</h2>
|
|
||||||
<p>
|
|
||||||
This is probably a bad idea but—
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Are you attending Isle of Wonders on Cres, Croatia, between 28. 6. and 30. 6.? So am I, by official duty.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Club Amulet D20 is forecasted to have a stand there, and I am forecasted to be in the general vicinity of it (barring any unexpected circumstances). I'll be either taking photos, painting minis, or doing heatstroke any% in rather rudamentary costume.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
If you're there, you can swing around to say 'hi' or provide some validation, or paint some minis. Rumor has it Conquest will have paint&take event.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
— Tamius
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<small>I am not paid to shill this.</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default({
|
|
||||||
props: [
|
|
||||||
'settings'
|
|
||||||
],
|
|
||||||
mounted() {
|
|
||||||
this.settings.active.whatsNewChecked = true;
|
|
||||||
this.settings.save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
p, li {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
small {
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
.text-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.donate {
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
background-color: #fa6;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,126 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h1>{{loggerPanel.title}}<small><br/>{{loggerPanel.subtitle}}</small></h1>
|
|
||||||
|
|
||||||
<div>Logger configuration:</div>
|
|
||||||
|
|
||||||
<template v-if="loggerPanel.pasteConfMode">
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<JsonObject
|
|
||||||
label="logger-settings"
|
|
||||||
:value="currentSettings"
|
|
||||||
:ignoreKeys="{'allowLogging': false}"
|
|
||||||
@change="updateSettingsUi"
|
|
||||||
></JsonObject>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<div class="button" @click="restoreLoggerSettings()">
|
|
||||||
Revert
|
|
||||||
</div>
|
|
||||||
<div class="button button-primary" @click="saveLoggerSettings()">
|
|
||||||
Save
|
|
||||||
</div>
|
|
||||||
<div class="button button-primary" @click="startLogging()">
|
|
||||||
Save and start logging
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import JsonObject from '../components/JsonEditor/JsonObject.vue'
|
|
||||||
import Logger, { baseLoggingOptions } from '../../../ext/lib/Logger';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
JsonObject
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loggerPanelRotation: [{
|
|
||||||
title: 'DEFORESTATOR 5000',
|
|
||||||
subtitle: 'Iron Legion\'s finest logging tool'
|
|
||||||
},{
|
|
||||||
title: 'Astinus',
|
|
||||||
subtitle: 'Ultrawidify logging tool'
|
|
||||||
}, {
|
|
||||||
title: 'Tracer',
|
|
||||||
subtitle: "Maybe I'll be Tracer — I'm already printing stack traces."
|
|
||||||
}, {
|
|
||||||
title: 'Grûmsh',
|
|
||||||
subtitle: "He who watches (all the things printed to the console)."
|
|
||||||
}, {
|
|
||||||
title: 'Situation Room/The Council',
|
|
||||||
subtitle: 'We will always be watching.'
|
|
||||||
}, {
|
|
||||||
title: 'Isengard Land Management Services',
|
|
||||||
subtitle: "#log4saruman"
|
|
||||||
}, {
|
|
||||||
title: 'Saw Hero',
|
|
||||||
subtitle: 'Ultrawidify logging tool'
|
|
||||||
}],
|
|
||||||
|
|
||||||
loggerPanel: {
|
|
||||||
title: 'whoopsie daisy',
|
|
||||||
subtitle: 'you broke the header choosing script',
|
|
||||||
pasteConfMode: false,
|
|
||||||
},
|
|
||||||
lastSettings: undefined,
|
|
||||||
parsedSettings: undefined,
|
|
||||||
currentSettings: undefined
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'eventBus',
|
|
||||||
'site'
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.loggerPanel = {
|
|
||||||
...this.loggerPanelRotation[Math.floor(Math.random() * this.loggerPanelRotation.length)],
|
|
||||||
pasteConfMode: false,
|
|
||||||
};
|
|
||||||
this.loadDefaultConfig();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
loadDefaultConfig() {
|
|
||||||
this.lastSettings = baseLoggingOptions;
|
|
||||||
this.parsedSettings = JSON.stringify(this.lastSettings, null, 2) || '';
|
|
||||||
this.currentSettings = JSON.parse(JSON.stringify(this.lastSettings));
|
|
||||||
},
|
|
||||||
async getLoggerSettings() {
|
|
||||||
this.lastSettings = await Logger.getConfig() || baseLoggingOptions;
|
|
||||||
this.parsedSettings = JSON.stringify(this.lastSettings, null, 2) || '';
|
|
||||||
this.currentSettings = JSON.parse(JSON.stringify(this.lastSettings));
|
|
||||||
},
|
|
||||||
updateSettingsUi(val) {
|
|
||||||
try {
|
|
||||||
this.parsedSettings = JSON.stringify(val, null, 2);
|
|
||||||
this.lastSettings = val;
|
|
||||||
this.currentSettings = JSON.parse(JSON.stringify(this.lastSettings));
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveLoggerSettings() {
|
|
||||||
Logger.saveConfig({...this.lastSettings});
|
|
||||||
},
|
|
||||||
restoreLoggerSettings() {
|
|
||||||
this.getLoggerSettings();
|
|
||||||
this.confHasError = false;
|
|
||||||
},
|
|
||||||
async startLogging(){
|
|
||||||
this.logStringified = undefined;
|
|
||||||
await Logger.saveConfig({...this.lastSettings, allowLogging: true});
|
|
||||||
window.location.reload();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>In-player UI</h1>
|
|
||||||
<div
|
|
||||||
class="button b3"
|
|
||||||
style="margin: 16px; padding: 4px;"
|
|
||||||
@click="showInPlayerUi()"
|
|
||||||
>
|
|
||||||
Show in-player UI
|
|
||||||
</div>
|
|
||||||
<p></p>
|
|
||||||
<p></p>
|
|
||||||
<p>In-player UI should show and hide automatically as you start or stop moving your mouse inside the player window.</p>
|
|
||||||
<p>Note that by default, in-player UI may not show if player window is not big enough.</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import UIProbeMixin from '../utils/UIProbeMixin';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [
|
|
||||||
UIProbeMixin
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'eventBus',
|
|
||||||
],
|
|
||||||
methods: {
|
|
||||||
showInPlayerUi() {
|
|
||||||
this.eventBus.send('uw-set-ui-state', {globalUiVisible: true}, {comms: {forwardTo: 'active'}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="">
|
|
||||||
<template v-if="!selectedSite">
|
|
||||||
<div style="margin-top: 1rem; margin-bottom: 1rem;">
|
|
||||||
<b>NOTE:</b> Sites not on this list use default extension settings.
|
|
||||||
</div>
|
|
||||||
<div v-for="site of sites" :key="site.key" @click="selectedSite = site.key" class="flex flex-col container pointer" style="margin-top: 4px; padding: 0.5rem 1rem;">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div class="flex-grow pointer">
|
|
||||||
<b>{{ site.key }}</b>
|
|
||||||
<span :style="getSiteTypeColor(site.type)">
|
|
||||||
(config: {{site.type ?? 'unknown'}})
|
|
||||||
</span></div>
|
|
||||||
<div>Edit</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<small>
|
|
||||||
Enabled: <span :style="getSiteEnabledColor(site.key, 'enable')"><small>{{ getSiteEnabledModes(site.key, 'enable') }}</small></span>;
|
|
||||||
Aard <span :style="getSiteEnabledColor(site.key, 'enableAard')"><small>{{ getSiteEnabledModes(site.key, 'enableAard') }}</small></span>;
|
|
||||||
kbd: <span :style="getSiteEnabledColor(site.key, 'enableKeyboard')"><small>{{ getSiteEnabledModes(site.key, 'enableKeyboard') }}</small></span>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="selectedSite">
|
|
||||||
<div class="flex flex-row container" style="align-items: center; color: #dedede; margin-top: 1rem;">
|
|
||||||
<div @click="selectedSite = null" class="pointer button-hover" style=" font-size: 2em; padding: 0.5rem; margin-right: 1em;">
|
|
||||||
←
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Editing {{ selectedSite }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<SiteExtensionSettings
|
|
||||||
v-if="selectedSiteSettings"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="selectedSiteSettings"
|
|
||||||
:isDefaultConfiguration="false"
|
|
||||||
></SiteExtensionSettings>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ExtensionMode from '../../../../../common/enums/ExtensionMode.enum';
|
|
||||||
import SiteExtensionSettings from './SiteExtensionSettings.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedSite: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
SiteExtensionSettings,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
sites() {
|
|
||||||
if (!this.settings?.active?.sites) {
|
|
||||||
return [];
|
|
||||||
} else {
|
|
||||||
const sites = [];
|
|
||||||
for (const siteKey in this.settings.active.sites) {
|
|
||||||
if (!siteKey.startsWith('@')) {
|
|
||||||
sites.push({
|
|
||||||
key: siteKey,
|
|
||||||
...this.settings.active.sites[siteKey]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sites.sort((a, b) => {
|
|
||||||
const cmpa = a.key.replace('www.', '');
|
|
||||||
const cmpb = b.key.replace('www.', '');
|
|
||||||
|
|
||||||
return cmpa < cmpb ? -1 : 1;
|
|
||||||
});
|
|
||||||
return sites;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedSiteSettings() {
|
|
||||||
return this.settings?.getSiteSettings(this.selectedSite) ?? null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getSiteTypeColor(siteType) {
|
|
||||||
switch (siteType) {
|
|
||||||
case 'official': return 'color: #fa6';
|
|
||||||
case 'community': return 'color: rgb(114, 114, 218)';
|
|
||||||
case 'officially-disabled': return 'color: #f00';
|
|
||||||
case 'testing': return 'color: #d81';
|
|
||||||
default: return 'color: rgb(138, 65, 126)'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getSiteEnabledColor(site, component) {
|
|
||||||
const status = this.getSiteEnabledModes(site, component);
|
|
||||||
|
|
||||||
return status === 'disabled' ? 'color: #f00' : 'color: #1f8';
|
|
||||||
},
|
|
||||||
getSiteEnabledModes(site, component) {
|
|
||||||
if (this.settings?.getSiteSettings(site).data[component]?.normal === ExtensionMode.Enabled) {
|
|
||||||
return 'always';
|
|
||||||
}
|
|
||||||
if (this.settings?.getSiteSettings(site).data[component]?.theater === ExtensionMode.Enabled) {
|
|
||||||
return 'T + FS';
|
|
||||||
}
|
|
||||||
if (this.settings?.getSiteSettings(site).data[component]?.fullscreen === ExtensionMode.Enabled) {
|
|
||||||
return 'fullscreen';
|
|
||||||
}
|
|
||||||
return 'disabled';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
|
||||||
@ -1,464 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- Enable extension -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Enable extension under the following conditions:
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="simpleExtensionSettings.enable"
|
|
||||||
@click="setExtensionMode('enable', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="simpleExtensionSettings.enable === 'complex'"
|
|
||||||
value="complex"
|
|
||||||
>
|
|
||||||
(Site uses advanced settings)
|
|
||||||
</option>
|
|
||||||
<template v-if="isDefaultConfiguration">
|
|
||||||
<option value="disabled">
|
|
||||||
Disabled (unless enabled for specific site)
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ()
|
|
||||||
</option>
|
|
||||||
<option value="disabled">
|
|
||||||
Never
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<option value="fs">
|
|
||||||
Fullscreen only
|
|
||||||
</option>
|
|
||||||
<option value="theater">
|
|
||||||
Fullscreen and theater mode
|
|
||||||
</option>
|
|
||||||
<option value="enabled">
|
|
||||||
Always
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Enable AARD -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Enable automatic aspect ratio detection under the following conditions:
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="simpleExtensionSettings.enableAard"
|
|
||||||
@click="setExtensionMode('enableAard', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="simpleExtensionSettings.enable === 'complex'"
|
|
||||||
value="complex"
|
|
||||||
>
|
|
||||||
(Site uses advanced settings)
|
|
||||||
</option>
|
|
||||||
<template v-if="isDefaultConfiguration">
|
|
||||||
<option value="disabled">
|
|
||||||
Disabled (unless enabled for specific site)
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ()
|
|
||||||
</option>
|
|
||||||
<option value="disabled">
|
|
||||||
Never
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<option value="fs">
|
|
||||||
Fullscreen only
|
|
||||||
</option>
|
|
||||||
<option value="theater">
|
|
||||||
Fullscreen and theater mode
|
|
||||||
</option>
|
|
||||||
<option value="enabled">
|
|
||||||
Always
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Enable keyboard -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Enable keyboard shortcuts under the following conditions
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="simpleExtensionSettings.enableKeyboard"
|
|
||||||
@click="setExtensionMode('enableKeyboard', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="simpleExtensionSettings.enable === 'complex'"
|
|
||||||
value="complex"
|
|
||||||
>
|
|
||||||
(Site uses advanced settings)
|
|
||||||
</option>
|
|
||||||
<template v-if="isDefaultConfiguration">
|
|
||||||
<option value="disabled">
|
|
||||||
Disabled (unless enabled for specific site)
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ()
|
|
||||||
</option>
|
|
||||||
<option value="disabled">
|
|
||||||
Never
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<option value="fs">
|
|
||||||
Fullscreen only
|
|
||||||
</option>
|
|
||||||
<option value="theater">
|
|
||||||
Fullscreen and theater mode
|
|
||||||
</option>
|
|
||||||
<option value="enabled">
|
|
||||||
Always
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Default crop -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default crop:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="siteDefaultCrop"
|
|
||||||
@change="setOption('defaults.crop', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="!isDefaultConfiguration"
|
|
||||||
:value="JSON.stringify({useDefault: true})"
|
|
||||||
>
|
|
||||||
Use default ({{getCommandValue(settings?.active.commands.crop, siteSettings.data.defaults.crop)}})
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="hint">This is how extension will crop video if/when autodetection is disabled. Pick 'Reset' option to keep aspect ratio as-is by default.</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Default stretch -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default stretch:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="siteDefaultStretch"
|
|
||||||
@change="setOption('defaults.stretch', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="!isDefaultConfiguration"
|
|
||||||
:value="JSON.stringify({useDefault: true})"
|
|
||||||
>
|
|
||||||
Use default ({{getCommandValue(settings?.active.commands.stretch, siteSettings.data.defaults.stretch)}})
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Default alignment -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default alignment:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="siteDefaultAlignment"
|
|
||||||
@change="setOption('defaults.alignment', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="!isDefaultConfiguration"
|
|
||||||
:value="JSON.stringify({useDefault: true})"
|
|
||||||
>
|
|
||||||
Use default ({{getAlignmentLabel(siteSettings.data.defaults.alignment)}})
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of alignmentOptions"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Crop, et. al. Persistence -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Persist crop, stretch, and alignment between videos</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="siteDefaultCropPersistence"
|
|
||||||
@click="setOption('persistCSA', $event)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-if="!isDefaultConfiguration"
|
|
||||||
:value="CropModePersistence.Default"
|
|
||||||
>
|
|
||||||
Use default ({{defaultPersistanceLabel}})
|
|
||||||
</option>
|
|
||||||
<option :value="CropModePersistence.Disabled">Disabled</option>
|
|
||||||
<option :value="CropModePersistence.UntilPageReload">Until page reload</option>
|
|
||||||
<option :value="CropModePersistence.CurrentSession">Current session</option>
|
|
||||||
<option :value="CropModePersistence.Forever">Always persist</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ExtensionMode from '../../../../../common/enums/ExtensionMode.enum';
|
|
||||||
import VideoAlignmentType from '../../../../../common/enums/VideoAlignmentType.enum';
|
|
||||||
import CropModePersistence from './../../../../../common/enums/CropModePersistence.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
CropModePersistence: CropModePersistence,
|
|
||||||
alignmentOptions: [
|
|
||||||
{label: 'Top left', arguments: {x: VideoAlignmentType.Left, y: VideoAlignmentType.Top}},
|
|
||||||
{label: 'Top center', arguments: {x: VideoAlignmentType.Center, y: VideoAlignmentType.Top}},
|
|
||||||
{label: 'Top right', arguments: {x: VideoAlignmentType.Right, y: VideoAlignmentType.Top}},
|
|
||||||
{label: 'Left', arguments: {x: VideoAlignmentType.Left, y: VideoAlignmentType.Center}},
|
|
||||||
{label: 'Center', arguments: {x: VideoAlignmentType.Center, y: VideoAlignmentType.Center}},
|
|
||||||
{label: 'Right', arguments: {x: VideoAlignmentType.Right, y: VideoAlignmentType.Center}},
|
|
||||||
{label: 'Bottom left', arguments: {x: VideoAlignmentType.Left, y: VideoAlignmentType.Bottom}},
|
|
||||||
{label: 'Bottom center', arguments: {x: VideoAlignmentType.Center, y: VideoAlignmentType.Bottom}},
|
|
||||||
{label: 'Bottom right', arguments: {x: VideoAlignmentType.Right, y: VideoAlignmentType.Bottom}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'siteSettings',
|
|
||||||
'isDefaultConfiguration'
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
simpleExtensionSettings() {
|
|
||||||
return {
|
|
||||||
enable: this.compileSimpleSettings('enable'),
|
|
||||||
enableAard: this.compileSimpleSettings('enableAard'),
|
|
||||||
enableKeyboard: this.compileSimpleSettings('enableKeyboard'),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
siteDefaultCrop() {
|
|
||||||
// console.log('Getting default site crop:', this.siteSettings.raw?.defaults?.crop ? 'yay' : '{useDefault}', this.siteSettings.raw?.defaults?.crop)
|
|
||||||
return this.siteSettings.raw?.defaults?.crop ? JSON.stringify(this.siteSettings.raw?.defaults?.crop) : JSON.stringify({useDefault: true});
|
|
||||||
},
|
|
||||||
siteDefaultStretch() {
|
|
||||||
return this.siteSettings.raw?.defaults?.stretch ? JSON.stringify(this.siteSettings.raw?.defaults?.stretch) : JSON.stringify({useDefault: true});
|
|
||||||
},
|
|
||||||
siteDefaultAlignment() {
|
|
||||||
return this.siteSettings.raw?.defaults?.alignment ? JSON.stringify(this.siteSettings.raw?.defaults?.alignment) : JSON.stringify({useDefault: true});
|
|
||||||
},
|
|
||||||
siteDefaultCropPersistence() {
|
|
||||||
return this.siteSettings.raw?.persistCSA ?? undefined;
|
|
||||||
},
|
|
||||||
defaultPersistanceLabel() {
|
|
||||||
switch (this.siteSettings.defaultSettings.persistCSA) {
|
|
||||||
case CropModePersistence.CurrentSession:
|
|
||||||
return 'current session';
|
|
||||||
case CropModePersistence.Disabled:
|
|
||||||
return 'disabled';
|
|
||||||
case CropModePersistence.UntilPageReload:
|
|
||||||
return 'until page reload';
|
|
||||||
case CropModePersistence.Forever:
|
|
||||||
return 'Always persist';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '??';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Compiles our extension settings into more user-friendly options
|
|
||||||
*/
|
|
||||||
compileSimpleSettings(component) {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
this.siteSettings?.data?.[component]?.normal === ExtensionMode.Disabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.theater === ExtensionMode.Disabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.fullscreen === ExtensionMode.Disabled
|
|
||||||
) {
|
|
||||||
return 'disabled';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.siteSettings?.data?.[component]?.normal === ExtensionMode.Default
|
|
||||||
&& this.siteSettings?.data?.[component]?.theater === ExtensionMode.Default
|
|
||||||
&& this.siteSettings?.data?.[component]?.fullscreen === ExtensionMode.Default
|
|
||||||
) {
|
|
||||||
return 'default';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.siteSettings?.data?.[component]?.normal === ExtensionMode.Disabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.theater === ExtensionMode.Disabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.fullscreen === ExtensionMode.Enabled
|
|
||||||
) {
|
|
||||||
return 'fs';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.siteSettings?.data?.[component]?.normal === ExtensionMode.Disabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.theater === ExtensionMode.Enabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.fullscreen === ExtensionMode.Enabled
|
|
||||||
) {
|
|
||||||
return 'theater';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.siteSettings?.data?.[component]?.normal === ExtensionMode.Enabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.theater === ExtensionMode.Enabled
|
|
||||||
&& this.siteSettings?.data?.[component]?.fullscreen === ExtensionMode.Enabled
|
|
||||||
) {
|
|
||||||
return 'enabled';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'complex';
|
|
||||||
} catch (e) {
|
|
||||||
return 'loading';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getCommandValue(availableCommands, command) {
|
|
||||||
for (const cmd of availableCommands) {
|
|
||||||
if (JSON.stringify(cmd.arguments) === JSON.stringify(command)) {
|
|
||||||
return cmd.label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'Unknown command';
|
|
||||||
},
|
|
||||||
|
|
||||||
getAlignmentLabel(alignment) {
|
|
||||||
// in case default settings don't have this set
|
|
||||||
if (!alignment) {
|
|
||||||
return 'Center'
|
|
||||||
}
|
|
||||||
|
|
||||||
let x, y;
|
|
||||||
if (alignment.x === VideoAlignmentType.Center) {
|
|
||||||
x = 'center';
|
|
||||||
} else if (alignment.x === VideoAlignmentType.Left) {
|
|
||||||
x = 'left';
|
|
||||||
} else if (alignment.x === VideoAlignmentType.Right) {
|
|
||||||
x = 'right';
|
|
||||||
} else {
|
|
||||||
x = '??'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alignment.y === VideoAlignmentType.Center) {
|
|
||||||
y = 'center';
|
|
||||||
} else if (alignment.y === VideoAlignmentType.Bottom) {
|
|
||||||
y = 'bottom';
|
|
||||||
} else if (alignment.y === VideoAlignmentType.Top) {
|
|
||||||
y = 'top';
|
|
||||||
} else {
|
|
||||||
y = '???'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (x === y) {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
return `${y} ${x}`;
|
|
||||||
},
|
|
||||||
getOption(option) {
|
|
||||||
|
|
||||||
},
|
|
||||||
async setOption(option, $event) {
|
|
||||||
const value = $event.target.value;
|
|
||||||
let commandArguments;
|
|
||||||
|
|
||||||
// if argument is json, parse json. Otherwise, pass the value as-is
|
|
||||||
try {
|
|
||||||
commandArguments = value !== undefined ? JSON.parse(value) : undefined;
|
|
||||||
} catch(e) {
|
|
||||||
commandArguments = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandArguments.useDefault) {
|
|
||||||
commandArguments = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('setting option', option, 'to cmd:', commandArguments, 'event data in:', value);
|
|
||||||
await this.siteSettings.set(option, commandArguments);
|
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
|
||||||
},
|
|
||||||
setExtensionMode(component, event) {
|
|
||||||
const option = event.target.value;
|
|
||||||
|
|
||||||
if (option === 'complex') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option === 'default') {
|
|
||||||
return this.siteSettings.set(component, {
|
|
||||||
normal: ExtensionMode.Default,
|
|
||||||
theater: ExtensionMode.Default,
|
|
||||||
fullscreen: ExtensionMode.Default
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (option === 'disabled') {
|
|
||||||
return this.siteSettings.set(component, {
|
|
||||||
normal: ExtensionMode.Disabled,
|
|
||||||
theater: ExtensionMode.Disabled,
|
|
||||||
fullscreen: ExtensionMode.Disabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (option === 'enabled') {
|
|
||||||
return this.siteSettings.set(component, {
|
|
||||||
normal: ExtensionMode.Enabled,
|
|
||||||
theater: ExtensionMode.Enabled,
|
|
||||||
fullscreen: ExtensionMode.Enabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (option === 'theater') {
|
|
||||||
return this.siteSettings.set(component, {
|
|
||||||
normal: ExtensionMode.Disabled,
|
|
||||||
theater: ExtensionMode.Enabled,
|
|
||||||
fullscreen: ExtensionMode.Enabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (option === 'fs') {
|
|
||||||
return this.siteSettings.set(component, {
|
|
||||||
normal: ExtensionMode.Disabled,
|
|
||||||
theater: ExtensionMode.Disabled,
|
|
||||||
fullscreen: ExtensionMode.Enabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
|
||||||
<style scoped>
|
|
||||||
.button-hover:hover {
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,236 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- <div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="32" />
|
|
||||||
<h1>Crop video:</h1>
|
|
||||||
</div> -->
|
|
||||||
<div class="sub-panel-content flex flex-row flex-wrap">
|
|
||||||
<ShortcutButton
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
class="flex b3 button"
|
|
||||||
:class="{active: editMode ? index === editModeOptions?.crop?.selectedIndex : isActiveCrop(command)}"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="editMode ? editAction(command, index, 'crop') : execAction(command)"
|
|
||||||
>
|
|
||||||
</ShortcutButton>
|
|
||||||
|
|
||||||
<!-- "Add new" button -->
|
|
||||||
<ShortcutButton
|
|
||||||
v-if="editMode"
|
|
||||||
class="button b3"
|
|
||||||
:class="{active: editMode ? editModeOptions?.crop?.selectedIndex === null : isActiveCrop(command)}"
|
|
||||||
label="Add new"
|
|
||||||
@click="editAction(
|
|
||||||
{action: 'set-ar', label: 'New aspect ratio', arguments: {type: AspectRatioType.Fixed}},
|
|
||||||
null,
|
|
||||||
'crop'
|
|
||||||
)"
|
|
||||||
></ShortcutButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- EDIT MODE PANEL -->
|
|
||||||
<div
|
|
||||||
v-if="editMode && !editModeOptions?.crop?.selected"
|
|
||||||
class="sub-panel-content"
|
|
||||||
>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
Click a button to edit
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editMode && editModeOptions?.crop?.selected" class="sub-panel-content">
|
|
||||||
<div class="edit-action-area-header">
|
|
||||||
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.crop?.selected?.label}}</b>
|
|
||||||
<template v-if="editModeOptions?.crop?.selectedIndex === null && editModeOptions?.crop?.selected?.label !== 'New aspect ratio'">(New ratio)</template>
|
|
||||||
</div>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<!-- Some options are only shown for type 4 (fixed) crops -->
|
|
||||||
<template v-if="editModeOptions?.crop?.selected?.arguments?.type === AspectRatioType.Fixed">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Ratio:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<!-- We do an ugly in order to avoid spamming functions down at the bottom -->
|
|
||||||
<input
|
|
||||||
v-model="editModeOptions.crop.selected.arguments.ratio"
|
|
||||||
@blur="editModeOptions.crop.selected.label === 'New aspect ratio' ? editModeOptions.crop.selected.label = editModeOptions.crop.selected.arguments.ratio : null"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
You can enter a ratio in width:height format (e.g. "21:9" or "1:2.39"), or just the factor
|
|
||||||
(in this case, "1:2.39" would become "2.39" and "21:9" would become "2.33"). You should enter
|
|
||||||
your numbers without quote marks. Number will be converted to factor form on save.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.crop.selected.label">
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- editing keyboard shortcuts is always allowed -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Shortcut:</div>
|
|
||||||
<div class="">
|
|
||||||
<EditShortcutButton
|
|
||||||
:shortcut="editModeOptions?.crop?.selected?.shortcut"
|
|
||||||
@shortcutChanged="updateSelectedShortcut($event, 'crop')"
|
|
||||||
>
|
|
||||||
</EditShortcutButton>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
<b>Note:</b> Your browser and OS already use certain key combinations that involve Ctrl and Meta (Windows) keys — and, to a lesser extent, Alt.
|
|
||||||
The extension doesn't (and cannot) check whether the keyboard shortcut you enter is actually free for you to use. The extension also won't override
|
|
||||||
any keyboard shortcuts defined by the site itself.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<div
|
|
||||||
v-if="editModeOptions?.crop?.selected?.arguments?.type === AspectRatioType.Fixed && editModeOptions?.crop?.selectedIndex !== null"
|
|
||||||
class="button"
|
|
||||||
@click="deleteAction('crop')"
|
|
||||||
>
|
|
||||||
<mdicon name="delete"></mdicon> Delete
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div class="button" @click="cancelEdit('crop')">Cancel</div>
|
|
||||||
<div class="button" @click="saveShortcut('crop')">
|
|
||||||
<mdicon name="floppy"></mdicon>
|
|
||||||
|
|
||||||
<template v-if="editModeOptions?.crop?.selectedIndex === null">Add</template>
|
|
||||||
<template v-else>Save</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default for this site</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="siteDefaultCrop"
|
|
||||||
@change="setDefaultCrop($event, 'site')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ShortcutButton from '../../../components/ShortcutButton.vue';
|
|
||||||
import EditShortcutButton from '../../../components/EditShortcutButton';
|
|
||||||
import EditModeMixin from '../../../utils/EditModeMixin';
|
|
||||||
import KeyboardShortcutParserMixin from '../../../utils/KeyboardShortcutParserMixin';
|
|
||||||
import CommsMixin from '../../../utils/CommsMixin';
|
|
||||||
import AspectRatioType from '../../../../../common/enums/AspectRatioType.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
|
|
||||||
return {
|
|
||||||
AspectRatioType: AspectRatioType,
|
|
||||||
|
|
||||||
// TODO: this should be mixin?
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
// ComputeActionsMixin,
|
|
||||||
EditModeMixin,
|
|
||||||
KeyboardShortcutParserMixin,
|
|
||||||
CommsMixin
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
'isEditing'
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
siteDefaultCrop() {
|
|
||||||
return JSON.stringify(
|
|
||||||
this.siteSettings.data.defaults.crop
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
isEditing(newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.enableEditMode();
|
|
||||||
} else {
|
|
||||||
this.disableEditMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Sets default crop, for either site or global
|
|
||||||
*/
|
|
||||||
setDefaultCrop($event, scope) {
|
|
||||||
const commandArguments = JSON.parse($event.target.value);
|
|
||||||
|
|
||||||
this.siteSettings.set('defaults.crop', commandArguments);
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a given crop command is the currently active one
|
|
||||||
*/
|
|
||||||
isActiveCrop(cropCommand) {
|
|
||||||
if (! this.resizerConfig.crop) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultCrop = this.siteSettings.data.defaults.crop;
|
|
||||||
|
|
||||||
if (cropCommand.arguments.type === AspectRatioType.Automatic) {
|
|
||||||
return this.resizerConfig.crop.type === AspectRatioType.Automatic
|
|
||||||
|| this.resizerConfig.crop.type === AspectRatioType.AutomaticUpdate
|
|
||||||
|| this.resizerConfig.crop.type === AspectRatioType.Initial && defaultCrop === AspectRatioType.Automatic;
|
|
||||||
}
|
|
||||||
if (cropCommand.arguments.type === AspectRatioType.Reset) {
|
|
||||||
return this.resizerConfig.crop.type === AspectRatioType.Reset
|
|
||||||
|| this.resizerConfig.crop.type === AspectRatioType.Initial && defaultCrop !== AspectRatioType.Automatic;
|
|
||||||
}
|
|
||||||
if (cropCommand.arguments.type === AspectRatioType.Fixed) {
|
|
||||||
return this.resizerConfig.crop.type === AspectRatioType.Fixed
|
|
||||||
&& this.resizerConfig.crop.ratio === cropCommand.arguments.ratio;
|
|
||||||
}
|
|
||||||
// only legacy options (fitw, fith) left to handle:
|
|
||||||
return cropCommand.arguments.type === this.resizerConfig.crop.type;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
|
||||||
@ -1,238 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="sub-panel-content flex flex-row flex-wrap">
|
|
||||||
<ShortcutButton
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
class="b3 button"
|
|
||||||
:class="{active: editMode ? index === editModeOptions?.stretch?.selectedIndex : isActiveStretch(command)}"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="editMode ? editAction(command, index, 'stretch') : execAction(command)"
|
|
||||||
>
|
|
||||||
</ShortcutButton>
|
|
||||||
|
|
||||||
<!-- "Add new" button -->
|
|
||||||
<ShortcutButton
|
|
||||||
v-if="editMode"
|
|
||||||
class="button b3"
|
|
||||||
label="Add new"
|
|
||||||
@click="editAction(
|
|
||||||
{action: 'set-stretch', label: 'Stretch to ...', arguments: {type: StretchType.FixedSource}},
|
|
||||||
null,
|
|
||||||
'stretch'
|
|
||||||
)"
|
|
||||||
></ShortcutButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- EDIT MODE PANEL -->
|
|
||||||
<div
|
|
||||||
v-if="editMode && !editModeOptions?.stretch?.selected"
|
|
||||||
class="sub-panel-content"
|
|
||||||
>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
Click a button to edit
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editMode && editModeOptions?.stretch?.selected" class="sub-panel-content">
|
|
||||||
<div class="edit-action-area-header">
|
|
||||||
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.stretch?.selected?.label}}</b>
|
|
||||||
<template v-if="editModeOptions?.stretch?.selectedIndex === null && editModeOptions?.stretch?.selected?.label !== 'Stretch to ...'">(New option)</template>
|
|
||||||
</div>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<!-- There are some special options for 'thin borders' -->
|
|
||||||
<template v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.Conditional">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Limit:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input
|
|
||||||
v-model="editModeOptions.stretch.selected.arguments.limit"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
If vertical borders would take up less than this much of screen width, the image will be stretched. If the borders are too thick, image will not be stretched.
|
|
||||||
Value of 1 means 100%. Value of 0.1 means vertical black bars can take up 10% of the width at most. There's no validation on this, use common sense.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Some options are only shown for type 5 (fixed) stretch -->
|
|
||||||
<template v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.FixedSource">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Ratio:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<!-- We do an ugly in order to avoid spamming functions down at the bottom -->
|
|
||||||
<input
|
|
||||||
v-model="editModeOptions.stretch.selected.arguments.ratio"
|
|
||||||
@blur="editModeOptions.stretch.selected.label === 'Stretch to ...' ? editModeOptions.stretch.selected.label = `Stretch to ${editModeOptions.stretch.selected.arguments.ratio}` : null"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
You can enter a ratio in width:height format (e.g. "21:9" or "1:2.39"), or just the factor
|
|
||||||
(in this case, "1:2.39" would become "2.39" and "21:9" would become "2.33"). You should enter
|
|
||||||
your numbers without quote marks. Number will be converted to factor form on save.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.stretch.selected.label">
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- editing keyboard shortcuts is always allowed -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Shortcut:</div>
|
|
||||||
<div class="">
|
|
||||||
<EditShortcutButton
|
|
||||||
:shortcut="editModeOptions?.stretch?.selected?.shortcut"
|
|
||||||
@shortcutChanged="updateSelectedShortcut($event, 'stretch')"
|
|
||||||
>
|
|
||||||
</EditShortcutButton>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
<b>Note:</b> Your browser and OS already use certain key combinations that involve Ctrl and Meta (Windows) keys — and, to a lesser extent, Alt.
|
|
||||||
The extension doesn't (and cannot) check whether the keyboard shortcut you enter is actually free for you to use. The extension also won't override
|
|
||||||
any keyboard shortcuts defined by the site itself.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<div
|
|
||||||
v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.FixedSource && editModeOptions?.stretch?.selectedIndex !== null"
|
|
||||||
class="button"
|
|
||||||
@click="deleteAction('stretch')"
|
|
||||||
>
|
|
||||||
<mdicon name="delete"></mdicon> Delete
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div class="button" @click="cancelEdit('stretch')">Cancel</div>
|
|
||||||
<div class="button" @click="saveShortcut('stretch')">
|
|
||||||
<mdicon name="floppy"></mdicon>
|
|
||||||
|
|
||||||
<template v-if="editModeOptions?.crop?.selectedIndex === null">Add</template>
|
|
||||||
<template v-else>Save</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default for this site:</div>
|
|
||||||
<div class="select">
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="siteDefaultStretchMode"
|
|
||||||
@change="setDefaultStretchingMode($event, 'site')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ShortcutButton from '../../../components/ShortcutButton.vue';
|
|
||||||
import EditShortcutButton from '../../../components/EditShortcutButton';
|
|
||||||
import EditModeMixin from '../../../utils/EditModeMixin';
|
|
||||||
import KeyboardShortcutParserMixin from '../../../utils/KeyboardShortcutParserMixin';
|
|
||||||
import CommsMixin from '../../../utils/CommsMixin';
|
|
||||||
import StretchType from '../../../../../common/enums/StretchType.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
exec: null,
|
|
||||||
StretchType: StretchType,
|
|
||||||
|
|
||||||
// TODO: this should be mixin?
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
// ComputeActionsMixin,
|
|
||||||
EditModeMixin,
|
|
||||||
KeyboardShortcutParserMixin,
|
|
||||||
CommsMixin
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
'isEditing'
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
siteDefaultStretch() {
|
|
||||||
return JSON.stringify(
|
|
||||||
this.siteSettings.data.defaults.stretch
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
isEditing(newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.enableEditMode();
|
|
||||||
} else {
|
|
||||||
this.disableEditMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Sets default stretching mode, for either site or global
|
|
||||||
*/
|
|
||||||
setDefaultStretchingMode($event, globalOrSite) {
|
|
||||||
const commandArguments = JSON.parse($event.target.value);
|
|
||||||
this.siteSettings.set('defaults.stretch', commandArguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a given stretch command is the currently active one
|
|
||||||
*/
|
|
||||||
isActiveStretch(stretchCommand) {
|
|
||||||
if (! this.resizerConfig.stretch) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const defaultCrop = this.settings.getDefaultStretch(this.site);
|
|
||||||
|
|
||||||
if ([StretchType.NoStretch, StretchType.Basic, StretchType.Hybrid, StretchType.Conditional, StretchType.Default].includes(stretchCommand.arguments.type)) {
|
|
||||||
return this.resizerConfig.stretch.type === stretchCommand.arguments.type;
|
|
||||||
}
|
|
||||||
return this.resizerConfig.crop.type === stretchCommand.arguments.type && this.resizerConfig.crop.ratio === stretchCommand.arguments.ratio;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<!--
|
|
||||||
min, max and value need to be implemented in js as this slider
|
|
||||||
should use logarithmic scale
|
|
||||||
-->
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<Button
|
|
||||||
v-if="zoomAspectRatioLocked"
|
|
||||||
label="Unlock aspect ratio"
|
|
||||||
icon="lock-open"
|
|
||||||
:fixedWidth="true"
|
|
||||||
@click="toggleZoomAr()"
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
v-else
|
|
||||||
label="Lock aspect ratio"
|
|
||||||
icon="lock"
|
|
||||||
:fixedWidth="true"
|
|
||||||
@click="toggleZoomAr()"
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<template v-if="zoomAspectRatioLocked">
|
|
||||||
<input id="_input_zoom_slider"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value)"
|
|
||||||
/>
|
|
||||||
<div style="overflow: auto" class="flex flex-row">
|
|
||||||
<div class="flex flex-grow medium-small x-pad-1em">
|
|
||||||
Zoom: {{getZoomForDisplay('x')}}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-nogrow flex-noshrink medium-small">
|
|
||||||
<a class="_zoom_reset x-pad-1em" @click="resetZoom()">reset</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div>Horizontal zoom</div>
|
|
||||||
<input id="_input_zoom_slider"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="4"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value, 'x')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>Vertical zoom</div>
|
|
||||||
<input id="_input_zoom_slider"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
:value="zoom.y"
|
|
||||||
@input="changeZoom($event.target.value, 'y')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div style="overflow: auto" class="flex flex-row">
|
|
||||||
<div class="flex flex-grow medium-small x-pad-1em">
|
|
||||||
Zoom: {{getZoomForDisplay('x')}} x {{getZoomForDisplay('y')}}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-nogrow flex-noshrink medium-small">
|
|
||||||
<a class="_zoom_reset x-pad-1em" @click="resetZoom()">reset</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
zoomAspectRatioLocked: true,
|
|
||||||
zoom: {
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: this should be mixin?
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
'isEditing'
|
|
||||||
],
|
|
||||||
methods: {
|
|
||||||
getZoomForDisplay(axis) {
|
|
||||||
// zoom is internally handled logarithmically, because we want to have x0.5, x1, x2, x4 ... magnifications
|
|
||||||
// spaced out at regular intervals. When displaying, we need to convert that to non-logarithmic values.
|
|
||||||
|
|
||||||
return `${(Math.pow(2, this.zoom[axis]) * 100).toFixed()}%`
|
|
||||||
},
|
|
||||||
toggleZoomAr() {
|
|
||||||
this.zoomAspectRatioLocked = !this.zoomAspectRatioLocked;
|
|
||||||
},
|
|
||||||
|
|
||||||
resetZoom() {
|
|
||||||
// we store zoom logarithmically on this component
|
|
||||||
this.zoom = {x: 0, y: 0};
|
|
||||||
|
|
||||||
// we do not use logarithmic zoom elsewhere
|
|
||||||
// todo: replace eventBus with postMessage to parent
|
|
||||||
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'});
|
|
||||||
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'});
|
|
||||||
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'y'});
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'x'});
|
|
||||||
},
|
|
||||||
changeZoom(newZoom, axis) {
|
|
||||||
// we store zoom logarithmically on this compnent
|
|
||||||
if (!axis) {
|
|
||||||
this.zoom.x = newZoom;
|
|
||||||
} else {
|
|
||||||
this.zoom[axis] = newZoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we do not use logarithmic zoom elsewhere, therefore we need to convert
|
|
||||||
newZoom = Math.pow(2, newZoom);
|
|
||||||
|
|
||||||
if (this.zoomAspectRatioLocked) {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'y'});
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'x'});
|
|
||||||
} else {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: axis ?? 'x'});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
|
||||||
@ -1,569 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col tab-root">
|
|
||||||
<!-- ADD ANY OPTION BARS HERE -->
|
|
||||||
|
|
||||||
<!-- The rest of the tab -->
|
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
|
|
||||||
<!-- Player element picker -->
|
|
||||||
<div class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h1><mdicon name="television-play" :size="32" /> Player element</h1>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div class="sub-panel-content">
|
|
||||||
<p>
|
|
||||||
You're probably on this page because Ultrawidify doesn't crop the player correctly.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
If you hover over the boxes below, the corresponding element will change (sepia filter + higher brightness + reduced contrast + it gets an outline). Player element
|
|
||||||
should be the closest element to the video element, for which the sepia/brightness effect covers the area you expect the video will cover.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You need to reload the page for changes to take effect.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- <p>
|
|
||||||
<a @click="showAdvancedOptions = !showAdvancedOptions">
|
|
||||||
<template v-if="showAdvancedOptions">Hide advanced options</template>
|
|
||||||
<template v-else>Show advanced options</template>
|
|
||||||
</a>
|
|
||||||
</p> -->
|
|
||||||
|
|
||||||
<div v-if="showAdvancedOptions" style="display: flex; flex-direction: row">
|
|
||||||
<div style="display: flex; flex-direction: column">
|
|
||||||
<div>
|
|
||||||
<input :checked="playerManualQs"
|
|
||||||
@change="togglePlayerManualQs"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
Use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors" target="_blank">CSS selector</a> for player<br/>
|
|
||||||
<small>If defining multiple selectors, separate them with commas.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>Selector</div>
|
|
||||||
<input type="text"
|
|
||||||
v-model="playerQs"
|
|
||||||
@change="updatePlayerQuerySelector"
|
|
||||||
@blur="updatePlayerQuerySelector"
|
|
||||||
:disabled="playerByNodeIndex || !playerManualQs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: column">
|
|
||||||
<b>Custom CSS for site</b>
|
|
||||||
<textarea></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; flex-direction: row;">
|
|
||||||
<div class="element-tree">
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<div class="status-relative">
|
|
||||||
Status <mdicon name="help-circle" @click="showLegend = !showLegend" />
|
|
||||||
|
|
||||||
<div v-if="showLegend" class="element-symbol-legend">
|
|
||||||
<b>Symbols:</b><br />
|
|
||||||
<mdicon name="alert-remove" class="invalid" /> Element of invalid dimensions<br />
|
|
||||||
<mdicon name="refresh-auto" class="auto-match" /> Ultrawidify's player detection thinks this should be the player<br />
|
|
||||||
<mdicon name="bookmark" class="parent-offset-match" /> Site settings say this should be the player (based on counting parents)<br />
|
|
||||||
<mdicon name="crosshairs" class="qs-match" /> Site settings say this should be the player (based on query selectors)<br />
|
|
||||||
<mdicon name="check-circle" class="activePlayer" /> Element that is actually the player
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th>Element</th>
|
|
||||||
<!-- <th>Actions</th> -->
|
|
||||||
<!-- <th>Quick fixes</th> -->
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="(element, index) of elementStack"
|
|
||||||
:key="index"
|
|
||||||
class="element-row"
|
|
||||||
>
|
|
||||||
<td>
|
|
||||||
<div class="status">
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.invalidSize"
|
|
||||||
class="invalid"
|
|
||||||
>
|
|
||||||
<mdicon name="alert-remove" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.autoMatch"
|
|
||||||
class="auto-match"
|
|
||||||
>
|
|
||||||
<mdicon name="refresh-auto" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.qsMatch"
|
|
||||||
class="qs-match"
|
|
||||||
>
|
|
||||||
<mdicon name="crosshairs" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.manualElementByParentIndex"
|
|
||||||
class="parent-offset-match"
|
|
||||||
>
|
|
||||||
<mdicon name="bookmark" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.activePlayer"
|
|
||||||
class="activePlayer"
|
|
||||||
>
|
|
||||||
<mdicon name="check-circle" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
class="element-data"
|
|
||||||
|
|
||||||
@mouseover="markElement(index, true)"
|
|
||||||
@mouseleave="markElement(index, false)"
|
|
||||||
|
|
||||||
@click="setPlayer(index)"
|
|
||||||
>
|
|
||||||
<div class="tag">
|
|
||||||
<b>{{element.tagName}}</b> <i class="id">{{element.id ? `#`:''}}{{element.id}}</i> @ <span class="dimensions">{{element.width}}</span>x<span class="dimensions">{{element.height}}</span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div v-if="element.classList" class="class-list">
|
|
||||||
<div v-for="cls of element.classList" :key="cls">
|
|
||||||
{{cls}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<!-- <div @click="designatePlayer(index)">Set as player {{ index }}</div> -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<!-- <td>
|
|
||||||
<div
|
|
||||||
class="css-fixes"
|
|
||||||
>
|
|
||||||
<div style="width: 100%"><b>Quick fixes:</b></div>
|
|
||||||
<div
|
|
||||||
class="css-line"
|
|
||||||
:class="{'active': cssStack[index]?.includes('width: 100%;')}"
|
|
||||||
@click="toggleCssForElement(index, 'width: 100%;')"
|
|
||||||
>
|
|
||||||
Width: 100%
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="css-line"
|
|
||||||
:class="{'active': cssStack[index]?.includes('height: 100%;')}"
|
|
||||||
@click="toggleCssForElement(index, 'height: 100%;')"
|
|
||||||
>
|
|
||||||
Height: 100%
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="css-line"
|
|
||||||
:class="{'active': cssStack[index]?.includes('display: flex;')}"
|
|
||||||
@click="toggleCssForElement(index, 'display: flex;')"
|
|
||||||
>
|
|
||||||
Display: flex
|
|
||||||
</div>
|
|
||||||
<div class="css-line">
|
|
||||||
Flex direction:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('flex-direction: row;')}"
|
|
||||||
@click="toggleCssForElement(index, 'flex-direction', 'row')"
|
|
||||||
>
|
|
||||||
row
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('flex-direction: column;')}"
|
|
||||||
@click="toggleCssForElement(index, 'flex-direction', 'column')"
|
|
||||||
>
|
|
||||||
column
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="css-line">
|
|
||||||
Justify content:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('justify-content: start;')}"
|
|
||||||
@click="toggleCssForElement(index, 'justify-content', 'start')"
|
|
||||||
>
|
|
||||||
start
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('justify-content: center;')}"
|
|
||||||
@click="toggleCssForElement(index, 'justify-content', 'center')"
|
|
||||||
>
|
|
||||||
center
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('justify-content: end;')}"
|
|
||||||
@click="toggleCssForElement(index, 'justify-content', 'end')"
|
|
||||||
>
|
|
||||||
end
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="css-line">
|
|
||||||
Align items:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('align-items: start;')}"
|
|
||||||
@click="toggleCssForElement(index, 'align-items', 'start')"
|
|
||||||
>
|
|
||||||
start
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('align-items: center;')}"
|
|
||||||
@click="toggleCssForElement(index, 'align-items', 'center')"
|
|
||||||
>
|
|
||||||
center
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('align-items: end;')}"
|
|
||||||
@click="toggleCssForElement(index, 'align-items', 'end')"
|
|
||||||
>
|
|
||||||
end
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="css-line">
|
|
||||||
Justify self:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('justify-self: start;')}"
|
|
||||||
@click="toggleCssForElement(index, 'justify-self', 'start')"
|
|
||||||
>
|
|
||||||
start
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('justify-self: center;')}"
|
|
||||||
@click="toggleCssForElement(index, 'justify-self', 'center')"
|
|
||||||
>
|
|
||||||
center
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('justify-self: end;')}"
|
|
||||||
@click="toggleCssForElement(index, 'justify-self', 'end')"
|
|
||||||
>
|
|
||||||
end
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="css-line">
|
|
||||||
Align self:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('align-self: start;')}"
|
|
||||||
@click="toggleCssForElement(index, 'align-self', 'start')"
|
|
||||||
>
|
|
||||||
start
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('align-self: center;')}"
|
|
||||||
@click="toggleCssForElement(index, 'align-self', 'center')"
|
|
||||||
>
|
|
||||||
center
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('align-self: end;')}"
|
|
||||||
@click="toggleCssForElement(index, 'align-self', 'end')"
|
|
||||||
>
|
|
||||||
end
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="css-line">
|
|
||||||
Text-align:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('text-align: left;')}"
|
|
||||||
@click="toggleCssForElement(index, 'text-align', 'left')"
|
|
||||||
>
|
|
||||||
left
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('text-align: center;')}"
|
|
||||||
@click="toggleCssForElement(index, 'text-align', 'center')"
|
|
||||||
>
|
|
||||||
center
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.find(x => x.includes('text-align: right'))}"
|
|
||||||
@click="toggleCssForElement(index, 'text-align', 'right')"
|
|
||||||
>
|
|
||||||
right
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="css-line">
|
|
||||||
Position:
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('position: relative;')}"
|
|
||||||
@click="toggleCssForElement(index, 'position', 'relative')"
|
|
||||||
>
|
|
||||||
relative
|
|
||||||
</span> |
|
|
||||||
<span
|
|
||||||
class="css-line-suboption"
|
|
||||||
:class="{'active': cssStack[index]?.includes('position: absolute;')}"
|
|
||||||
@click="toggleCssForElement(index, 'position', 'absolute')"
|
|
||||||
>
|
|
||||||
absolute
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td> -->
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="element-config">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="css-preview">
|
|
||||||
{{cssStack}}
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="sub-panel-content">
|
|
||||||
<h2>Advanced settings</h2>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
|
|
||||||
export default({
|
|
||||||
components: {
|
|
||||||
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
elementStack: [],
|
|
||||||
cssStack: [],
|
|
||||||
showLegend: false,
|
|
||||||
showAdvancedOptions: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mixins: [],
|
|
||||||
props: [
|
|
||||||
'siteSettings',
|
|
||||||
'frame',
|
|
||||||
'eventBus',
|
|
||||||
'site',
|
|
||||||
'isPopup'
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleElementStack(config)});
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getPlayerTree();
|
|
||||||
},
|
|
||||||
computed: {},
|
|
||||||
methods: {
|
|
||||||
getPlayerTree() {
|
|
||||||
if (this.isPopup) {
|
|
||||||
this.eventBus.send('get-player-tree');
|
|
||||||
} else {
|
|
||||||
this.eventBus.sendToTunnel('get-player-tree');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleElementStack(configBroadcast) {
|
|
||||||
if (configBroadcast.type === 'player-tree') {
|
|
||||||
this.elementStack = configBroadcast.config;
|
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
markElement(parentIndex, enable) {
|
|
||||||
this.eventBus.sendToTunnel('set-mark-element', {parentIndex, enable});
|
|
||||||
},
|
|
||||||
async setPlayer(index) {
|
|
||||||
// yup.
|
|
||||||
this.siteSettings.getDOMConfig('modified', 'original');
|
|
||||||
await this.siteSettings.setUpdateFlags(['PlayerData']);
|
|
||||||
await this.siteSettings.set('DOMConfig.modified.type', 'modified', {noSave: true});
|
|
||||||
await this.siteSettings.set('activeDOMConfig', 'modified', {noSave: true});
|
|
||||||
|
|
||||||
// if user agrees with ultrawidify on what element player should be,
|
|
||||||
// we just unset our settings for this site
|
|
||||||
if (this.elementStack[index].heuristics?.autoMatch) {
|
|
||||||
await this.siteSettings.set('DOMConfig.modified.elements.player.manual', false);
|
|
||||||
this.getPlayerTree();
|
|
||||||
} else {
|
|
||||||
// ensure settings exist:
|
|
||||||
await this.siteSettings.set('DOMConfig.modified.elements.player.manual', true, {noSave: true});
|
|
||||||
await this.siteSettings.set('DOMConfig.modified.elements.player.mode', 'index', {noSave: true});
|
|
||||||
await this.siteSettings.set('DOMConfig.modified.elements.player.index', index, true);
|
|
||||||
this.getPlayerTree();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Toggles active CSS for element of certain parent index.
|
|
||||||
* cssValue is optional and can be included in cssRule argument
|
|
||||||
*/
|
|
||||||
toggleCssForElement(index, cssRule, cssValue) {
|
|
||||||
// we will handle elements that put cssValue as a separate argument elsewhere
|
|
||||||
if (cssValue) {
|
|
||||||
return this.toggleCssForElement_3arg(index,cssRule, cssValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this rule applies to current element — remove it!
|
|
||||||
if (this.cssStack[index]?.includes(cssRule)) {
|
|
||||||
this.cssStack[index] = this.cssStack[index].filter(x => ! x.includes(cssRule));
|
|
||||||
} else {
|
|
||||||
if (!this.cssStack[index]) {
|
|
||||||
this.cssStack[index] = [];
|
|
||||||
}
|
|
||||||
this.cssStack[index].push(cssRule)
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: update settings!
|
|
||||||
},
|
|
||||||
toggleCssForElement_3arg(index, cssRule, cssValue) {
|
|
||||||
const matching = this.cssStack[index]?.find(x => x.includes(cssRule))
|
|
||||||
if (matching) {
|
|
||||||
this.cssStack[index] = this.cssStack[index].filter(x => ! x.includes(cssRule));
|
|
||||||
if (!matching.includes(cssValue)) {
|
|
||||||
this.cssStack[index].push(`${cssRule}: ${cssValue};`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!this.cssStack[index]) {
|
|
||||||
this.cssStack[index] = [];
|
|
||||||
}
|
|
||||||
this.cssStack[index].push(`${cssRule}: ${cssValue};`);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: update settings!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
p {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.element-tree {
|
|
||||||
.element-row {
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: row;
|
|
||||||
margin: 0.5rem;
|
|
||||||
|
|
||||||
.status {
|
|
||||||
width: 6.9rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.invalid {
|
|
||||||
color: #f00;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.element-data {
|
|
||||||
border: 1px solid rgba(255,255,255,0.5);
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
|
|
||||||
max-width: 30rem;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
text-transform: lowercase;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.id {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.class-list {
|
|
||||||
font-style: italic;
|
|
||||||
opacity: 0.75;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
background-color: rgba(255,255,255,0.5);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
padding: 0.125rem 0.5rem;
|
|
||||||
margin: 0.125rem 0.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dimensions {
|
|
||||||
color: #473c85;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// .css-fixes {
|
|
||||||
// // display: flex;
|
|
||||||
// // flex-direction: row;
|
|
||||||
// // flex-wrap: wrap;
|
|
||||||
// // align-items: flex-start;
|
|
||||||
// // justify-content:flex-start;
|
|
||||||
// }
|
|
||||||
.css-line {
|
|
||||||
display: inline-block;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border: 1px solid rgba(255,255,255,0.5);
|
|
||||||
background: #000;
|
|
||||||
margin: 0.125rem 0.25rem;
|
|
||||||
padding: 0.125rem 0.25rem;
|
|
||||||
|
|
||||||
&.active, >span.active {
|
|
||||||
background: rgb(255, 174, 107);
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-relative {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.element-symbol-legend {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
z-index: 20000;
|
|
||||||
|
|
||||||
width: 32rem;
|
|
||||||
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
background-color: #000;
|
|
||||||
padding: 1rem;
|
|
||||||
border: 1px solid rgba(255,255,255,0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-root {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,157 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="panel-root">
|
|
||||||
<h1>Resizer debug data</h1>
|
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
<div class="panel">
|
|
||||||
<h3 class="panel-title">
|
|
||||||
Player info
|
|
||||||
</h3>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Window size:
|
|
||||||
<small>(Inner size)</small>
|
|
||||||
</div>
|
|
||||||
<div class="data">{{windowWidth}} x {{windowHeight}}</div>
|
|
||||||
<div class="button" @click="refreshWindowSize()">
|
|
||||||
<!-- <Icon icon="arrow-clockwise"></Icon> -->
|
|
||||||
Refresh</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Player dimensions:</div>
|
|
||||||
<div class="data">{{debugData?.resizer?.playerData?.dimensions?.width ?? 'not detected'}} x {{debugData?.resizer?.playerData?.dimensions?.height ?? 'not detected'}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Is fullscreen?</div>
|
|
||||||
<div class="data">{{debugData?.resizer?.playerData?.dimensions?.fullscreen ?? 'unknown'}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Player id | classlist</div>
|
|
||||||
<div class="data">{{debugData?.resizer?.playerData?.elementId || '<no ID>'}} | {{debugData?.resizer?.playerData?.classList || '<no classes>'}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Stream info -->
|
|
||||||
<div class="panel">
|
|
||||||
<h3 class="panel-title">
|
|
||||||
Stream info
|
|
||||||
</h3>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">
|
|
||||||
Stream dimensions: <small>(Native size of the video)</small>
|
|
||||||
</div>
|
|
||||||
<div class="data">
|
|
||||||
{{debugData?.resizer?.videoRawData?.streamDimensions.x}} x {{debugData?.resizer?.videoRawData?.streamDimensions?.y}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">
|
|
||||||
Stream displayed dimensions: <small>(Video file is being upscaled to this size)</small>
|
|
||||||
</div>
|
|
||||||
<div class="data">
|
|
||||||
{{debugData?.resizer?.videoRawData?.displayedSize?.x.toFixed()}} x {{debugData?.resizer?.videoRawData?.displayedSize?.y.toFixed()}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Video element native size: <small>(Size of the html element. Should be same as above most of the time!)</small></div>
|
|
||||||
<div class="data">{{debugData?.resizer?.videoRawData?.displayedSize?.x.toFixed(1)}} x {{debugData?.resizer?.videoRawData?.displayedSize?.y.toFixed(1)}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Size difference to player (raw): <small>(positive numbers: video element is bigger than player element)</small></div>
|
|
||||||
<div class="data">x: {{debugData?.resizer?.sizeDifferenceToPlayer?.beforeZoom?.wdiff.toFixed(1)}}; y: {{debugData?.resizer?.sizeDifferenceToPlayer?.beforeZoom?.hdiff.toFixed(1)}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Video element size (post zoom): <small>(Size of the html element after transform:scale is applied. Or at least, that's what Resizer::computeOffsets() thinks the final size is.)</small></div>
|
|
||||||
<div class="data">{{debugData?.resizer?.transformedSize?.x.toFixed(2)}} x {{debugData?.resizer?.transformedSize?.y.toFixed(2)}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title"><b>Size difference to player (post-zoom):</b> <small>(same as above, except after cropping, stretching, panning and zoom are applied)</small></div>
|
|
||||||
<div class="data">x: {{debugData?.resizer?.sizeDifferenceToPlayer?.afterZoom?.wdiff.toFixed(2)}}; y: {{debugData?.resizer?.sizeDifferenceToPlayer?.afterZoom?.hdiff.toFixed(2)}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Transform info -->
|
|
||||||
<div class="panel">
|
|
||||||
<h3 class="panel-title">
|
|
||||||
Transformations
|
|
||||||
</h3>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Alignment: <small>(I agree that 'left' and 'right' are both evil, but that's not the kind of alignments we're thinking of)</small></div>
|
|
||||||
<div class="data">{{debugData?.resizer?.videoTransform?.alignment || '<unknown>'}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Scale factor</div>
|
|
||||||
<div class="data">x: {{debugData?.resizer?.videoTransform?.scale.x.toFixed(2)}}; y: {{debugData?.resizer?.videoTransform?.scale.y.toFixed(2)}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="data-item">
|
|
||||||
<div class="data-title">Translation</div>
|
|
||||||
<div class="data">x: {{debugData?.resizer?.videoTransform?.translate.x.toFixed(2)}}; y: {{debugData?.resizer?.videoTransform?.translate.y.toFixed(2)}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="uw-debug-info flex"> -->
|
|
||||||
<!-- <pre> -->
|
|
||||||
<!-- {{debugDataPrettified}} -->
|
|
||||||
<!-- </pre> -->
|
|
||||||
<!-- </div> -->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// import Icon from '../../common/components/Icon';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
// Icon,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
debugData: Object
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
windowWidth: window.innerWidth,
|
|
||||||
windowHeight: window.innerHeight
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
refreshWindowSize() {
|
|
||||||
this.windowWidth = window.innerWidth;
|
|
||||||
this.windowHeight = window.innerHeight;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../../res/css/uwui-base.scss';
|
|
||||||
@import '../../../res/css/colors.scss';
|
|
||||||
@import '../../../res/css/font/overpass.css';
|
|
||||||
@import '../../../res/css/font/overpass-mono.css';
|
|
||||||
@import '../../../res/css/common.scss';
|
|
||||||
|
|
||||||
.panel-root {
|
|
||||||
display: block;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
display: inline-block;
|
|
||||||
width: 420px;
|
|
||||||
max-width: 100%;
|
|
||||||
padding: 12px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-item {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.69rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-title {
|
|
||||||
|
|
||||||
small {
|
|
||||||
display: block;
|
|
||||||
opacity: 0.69;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.data {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,192 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col" style="position: relative; width: 100%;">
|
|
||||||
<!-- 'Change UI' options is a tiny bit in upper right corner. -->
|
|
||||||
<div
|
|
||||||
class="options-bar flex flex-row"
|
|
||||||
:class="{isEditing: editMode}"
|
|
||||||
>
|
|
||||||
<template v-if="editMode">
|
|
||||||
<div style="height: 100%; display: flex; flex-direction: column; justify-content: center; flex: 0 0; padding-right: 8px;">
|
|
||||||
<mdicon name="alert" size="32" />
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow">
|
|
||||||
You are currently editing options and shortcuts.<br/>
|
|
||||||
<b>NOTE: changes will take effect after page reload.</b>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex-nogrow flex-noshrink"
|
|
||||||
@click="editMode = !editMode"
|
|
||||||
>
|
|
||||||
Exit edit mode
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div
|
|
||||||
class=""
|
|
||||||
@click="editMode = !editMode"
|
|
||||||
>
|
|
||||||
Edit ratios and shortcuts
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- The rest of the tab is under 'edit ratios and shortcuts' row -->
|
|
||||||
<div class="flex flex-row flex-wrap" style="width: 100%">
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<!-- CROP OPTIONS -->
|
|
||||||
<div v-if="settings" class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="32" />
|
|
||||||
<h1>Crop video:</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CropOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="editMode"
|
|
||||||
>
|
|
||||||
</CropOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STRETCH OPTIONS -->
|
|
||||||
<div v-if="settings" class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="stretch-to-page-outline" :size="32" />
|
|
||||||
<h1>Stretch video:</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StretchOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="editMode"
|
|
||||||
></StretchOptionsPanel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
|
||||||
|
|
||||||
<!-- VIDEO ALIGNMENT -->
|
|
||||||
<div class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="align-horizontal-center" :size="32" />
|
|
||||||
<h1>Video alignment:</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row justify-center mt-4">
|
|
||||||
<alignment-options-control-component
|
|
||||||
:eventBus="eventBus"
|
|
||||||
>
|
|
||||||
</alignment-options-control-component>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="flex flex-row flex-wrap">
|
|
||||||
<div class="m-t-0-33em display-block">
|
|
||||||
<input id="_input_zoom_site_allow_pan"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
Pan with mouse
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ZOOM OPTIONS -->
|
|
||||||
<!-- <div class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="magnify-plus-outline" :size="32" />
|
|
||||||
<h1>Manual zoom:</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ZoomOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="editMode"
|
|
||||||
></ZoomOptionsPanel>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ZoomOptionsPanel from './PanelComponents/VideoSettings/ZoomOptionsPanel.vue'
|
|
||||||
import CropOptionsPanel from './PanelComponents/VideoSettings/CropOptionsPanel'
|
|
||||||
import StretchOptionsPanel from './PanelComponents/VideoSettings/StretchOptionsPanel'
|
|
||||||
import Button from '../components/Button.vue'
|
|
||||||
import ShortcutButton from '../components/ShortcutButton';
|
|
||||||
import EditShortcutButton from '../components/EditShortcutButton';
|
|
||||||
import ComputeActionsMixin from '../mixins/ComputeActionsMixin';
|
|
||||||
import BrowserDetect from '../../../ext/conf/BrowserDetect';
|
|
||||||
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
|
|
||||||
import CommsMixin from '../utils/CommsMixin';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
exec: null,
|
|
||||||
scope: 'page',
|
|
||||||
|
|
||||||
editMode: false,
|
|
||||||
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
ComputeActionsMixin,
|
|
||||||
CommsMixin,
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'frame',
|
|
||||||
'eventBus',
|
|
||||||
'site'
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.eventBus.sendToTunnel('get-ar');
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
Button,
|
|
||||||
AlignmentOptionsControlComponent,
|
|
||||||
StretchOptionsPanel,
|
|
||||||
CropOptionsPanel, ZoomOptionsPanel
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
async openOptionsPage() {
|
|
||||||
BrowserDetect.runtime.openOptionsPage();
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../res-common/common.scss" scoped module></style>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.justify-center {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.items-center {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.mt-4{
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,247 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="action">
|
|
||||||
<div class="flex-row action-name-cmd-container">
|
|
||||||
<div class="">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="flex action-name">
|
|
||||||
<span v-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.userAdded || (action.cmd[0].arg === AspectRatioType.Fixed)" class="icon red" @click="removeAction()">🗙</span>
|
|
||||||
<span v-else class="icon transparent">🗙</span>
|
|
||||||
<span class="icon" @click="editAction()">🖉</span>
|
|
||||||
{{action.name}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="command-details">
|
|
||||||
<div class="flex flex-col cmd-container cd-pad">
|
|
||||||
<div class="flex bold">
|
|
||||||
Command:
|
|
||||||
</div>
|
|
||||||
<div class="flex cmdlist">
|
|
||||||
{{parseCommand(action.cmd)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- SCOPES -->
|
|
||||||
<div class="flex flex-col scopes-container cd-pad">
|
|
||||||
<div class="flex bold">Popup tabs:</div>
|
|
||||||
|
|
||||||
<!-- global scope -->
|
|
||||||
<div v-if="action.scopes.global"
|
|
||||||
class="flex flex-row scope-row"
|
|
||||||
>
|
|
||||||
<div class="flex scope-scope">
|
|
||||||
Global:
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label scope-visible">
|
|
||||||
Visible? <span class="scope-row-highlight">{{action.scopes.global.show ? 'Yes' : 'No'}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.scopes.global.show"
|
|
||||||
class="flex scope-row-label scope-button-label"
|
|
||||||
>
|
|
||||||
Button label?
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.global.label ? action.scopes.global.label : (action.label ? action.label : '')}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label">
|
|
||||||
Keyboard shortcut:
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.global.shortcut ? parseActionShortcut(action.scopes.global.shortcut[0]) : 'None'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- site scope -->
|
|
||||||
<div v-if="action.scopes.site"
|
|
||||||
class="flex flex-row scope-row"
|
|
||||||
>
|
|
||||||
<div class="flex scope-scope">
|
|
||||||
Site:
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label scope-visible">
|
|
||||||
Visible? <span class="scope-row-highlight">{{action.scopes.site.show ? 'Yes' : 'No'}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.scopes.site.show"
|
|
||||||
class="flex scope-row-label scope-button-label"
|
|
||||||
>
|
|
||||||
Button label?
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.site.label ? action.scopes.site.label : (action.label ? action.label : '')}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label">
|
|
||||||
Keyboard shortcut:
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.site.shortcut ? parseActionShortcut(action.scopes.site.shortcut[0]) : 'None'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- page scope -->
|
|
||||||
<div v-if="action.scopes.page"
|
|
||||||
class="flex flex-row scope-row"
|
|
||||||
>
|
|
||||||
<div class="flex scope-scope">
|
|
||||||
Page:
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label scope-visible">
|
|
||||||
Visible? <span class="scope-row-highlight">{{action.scopes.page.show ? 'Yes' : 'No'}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.scopes.page.show"
|
|
||||||
class="flex scope-row-label scope-button-label"
|
|
||||||
>
|
|
||||||
Button label?
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.page.label ? action.scopes.page.label : (action.label ? action.label : '')}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label">
|
|
||||||
Keyboard shortcut:
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.page.shortcut ? parseActionShortcut(action.scopes.page.shortcut[0]) : 'None'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import StretchType from '../enums/StretchType.enum';
|
|
||||||
import AspectRatioType from '../enums/AspectRatioType.enum';
|
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
StretchType: StretchType,
|
|
||||||
AspectRatioType: AspectRatioType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
action: Object,
|
|
||||||
index: Number,
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
parseActionShortcut(shortcut) {
|
|
||||||
return KeyboardShortcutParser.parseShortcut(shortcut);
|
|
||||||
},
|
|
||||||
parseCommand(cmd) {
|
|
||||||
let cmdstring = '';
|
|
||||||
for (const c of cmd) {
|
|
||||||
cmdstring += `${c.action} ${c.arg}${c.persistent ? ' persistent' : ''}; `;
|
|
||||||
}
|
|
||||||
return cmdstring;
|
|
||||||
},
|
|
||||||
editAction() {
|
|
||||||
this.$emit('edit');
|
|
||||||
},
|
|
||||||
removeAction() {
|
|
||||||
this.$emit('remove');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../res/css/colors.scss';
|
|
||||||
|
|
||||||
.action {
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action .command-details {
|
|
||||||
height: 0px;
|
|
||||||
max-height: 0px;
|
|
||||||
transition: max-height 0.5s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: height 0.5s ease;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action:hover .command-details {
|
|
||||||
height: auto;
|
|
||||||
max-height: 200px;
|
|
||||||
transition: max-height 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-details {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-name-cmd-container, .p1rem {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cd-pad {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-name {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 300;
|
|
||||||
color: $text-normal;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-name:hover, .action:hover .action-name {
|
|
||||||
color: lighten($primary-color, 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
|
||||||
color: $primary-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-container {
|
|
||||||
width: 13.37rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmdlist {
|
|
||||||
font-family: 'Overpass Mono';
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: $text-dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-scope {
|
|
||||||
width: 5rem;
|
|
||||||
text-align: right !important;
|
|
||||||
color: $secondary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-visible {
|
|
||||||
width: 7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-button-label {
|
|
||||||
width: 16rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-row-label {
|
|
||||||
color: $text-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-row-highlight {
|
|
||||||
color: $text-normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transparent {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
<template>
|
|
||||||
<tr>
|
|
||||||
<td class="cmd monospace">{{parseCommand(action.cmd)}}</td>
|
|
||||||
<td class="">{{action.label ? action.label : ""}}</td>
|
|
||||||
<td class="shortcut center-text">{{parseActionShortcut(action)}}</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': (action.shortcut && action.shortcut.length &&
|
|
||||||
(action.shortcut[0].onMouseMove || action.shortcut[0].onClick ||
|
|
||||||
action.shortcut[0].onScroll))}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.popup_global}"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.popup_site}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.popup}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.ui}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>{{action.ui_path ? action.ui_path : ""}}</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import StretchType from '../enums/StretchType.enum';
|
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
StretchType: StretchType
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
action: Object
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
parseActionShortcut(action) {
|
|
||||||
return KeyboardShortcutParser.parseShortcut(action.shortcut[0]);
|
|
||||||
},
|
|
||||||
parseCommand(cmd) {
|
|
||||||
let cmdstring = '';
|
|
||||||
for(const c of cmd) {
|
|
||||||
cmdstring += `${c.action} ${c.arg}${c.persistent ? ' persistent' : ''}; `;
|
|
||||||
}
|
|
||||||
return cmdstring;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="button center-text"
|
|
||||||
:class="{'setting-selected': selected }"
|
|
||||||
>
|
|
||||||
<div class="label">
|
|
||||||
{{label}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
fixedWidth: Boolean,
|
|
||||||
selected: Boolean,
|
|
||||||
label: String,
|
|
||||||
icon: String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.button {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: none;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
text-align: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col json-level-indent">
|
|
||||||
<div class="flex flex-row" @click="expanded_internal = !expanded_internal">
|
|
||||||
<div v-if="value_internal.key" class="item-key">
|
|
||||||
"{{value_internal.key}}" <b>:</b>
|
|
||||||
<span v-if="!expanded_internal"><b> [</b> ... <b>]</b>,</span>
|
|
||||||
<template v-else><b>[</b></template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="(row, key) of value_internal.value"
|
|
||||||
:key="key"
|
|
||||||
>
|
|
||||||
<JsonArray v-if="Array.isArray(row)"
|
|
||||||
:value="row"
|
|
||||||
:ignoreKeys="ignoreKeys"
|
|
||||||
@change="changeItem(rowKey, $event)"
|
|
||||||
>
|
|
||||||
</JsonArray>
|
|
||||||
<JsonObject v-else-if="typeof row === 'object' && row !== null"
|
|
||||||
:value="row"
|
|
||||||
:label="rowKey"
|
|
||||||
:ignoreKeys="ignoreKeys"
|
|
||||||
@change="changeItem(rowKey, $event)"
|
|
||||||
>
|
|
||||||
</JsonObject>
|
|
||||||
<JsonElement v-else
|
|
||||||
:value="row"
|
|
||||||
:label="rowKey"
|
|
||||||
@change="changeItem(rowKey, $event)"
|
|
||||||
>
|
|
||||||
</JsonElement>
|
|
||||||
</div>
|
|
||||||
<div v-if="expanded_internal"><b>],</b></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import JsonObject from './JsonObject';
|
|
||||||
import JsonElement from './JsonElement';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'JsonArray',
|
|
||||||
props: [
|
|
||||||
'value',
|
|
||||||
'expanded',
|
|
||||||
'ignoreKeys', // this prop is passthrough for JsonArray
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
JsonObject,
|
|
||||||
JsonElement,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
value_internal: undefined,
|
|
||||||
expanded_internal: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.value_internal = this.value;
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
value: function(val) {
|
|
||||||
this.value_internal = val;
|
|
||||||
},
|
|
||||||
expanded: function(val) {
|
|
||||||
if (val !== undefined && val !== null) {
|
|
||||||
this.expanded_internal = !!val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
changeItem(key, value) {
|
|
||||||
this.value_internal[key] = value;
|
|
||||||
this.$emit('change', this.value_internal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped src="./json.scss">
|
|
||||||
</style>
|
|
||||||
<style lang="scss" scoped src="../../../res/css/flex.scss">
|
|
||||||
</style>
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-row json-level-indent">
|
|
||||||
<div>
|
|
||||||
<b>
|
|
||||||
<span v-if="label" class="item-key"
|
|
||||||
:class="{'item-key-boolean-false': typeof value_internal === 'boolean' && !value_internal}"
|
|
||||||
>
|
|
||||||
"{{label}}"
|
|
||||||
</span>
|
|
||||||
:
|
|
||||||
</b>
|
|
||||||
</div>
|
|
||||||
<div v-if="typeof value_internal === 'boolean'"
|
|
||||||
:class="{'json-value-boolean-true': value_internal, 'json-value-boolean-false': !value_internal}"
|
|
||||||
@click="toggleBoolean"
|
|
||||||
>
|
|
||||||
<template v-if="value_internal">true</template>
|
|
||||||
<template v-else>false</template>
|
|
||||||
</div>
|
|
||||||
<div v-else
|
|
||||||
class="flex flex-row inline-edit"
|
|
||||||
:class="{
|
|
||||||
'json-value-number': typeof value_internal === 'number',
|
|
||||||
'json-value-string': typeof value_internal === 'string'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template v-if="typeof value_internal === 'string'">
|
|
||||||
"<div ref="element-edit-area"
|
|
||||||
contenteditable="true"
|
|
||||||
@keydown.enter="changeValue"
|
|
||||||
>
|
|
||||||
{{value_internal}}
|
|
||||||
</div>"
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div ref="element-edit-area"
|
|
||||||
contenteditable="true"
|
|
||||||
@keydown.enter="changeValue"
|
|
||||||
>
|
|
||||||
{{value_internal}}
|
|
||||||
</div>
|
|
||||||
</template>,
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'json-element',
|
|
||||||
props: [
|
|
||||||
'value',
|
|
||||||
'label',
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
value_internal: undefined,
|
|
||||||
editing: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.value_internal = this.value;
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
value: function(val) {
|
|
||||||
this.value_internal = val;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleBoolean() {
|
|
||||||
this.value_internal = !this.value_internal;
|
|
||||||
this.$emit('change', this.value_internal);
|
|
||||||
},
|
|
||||||
changeValue() {
|
|
||||||
this.$refs['element-edit-area'].blur();
|
|
||||||
const newValue = this.$refs['element-edit-area'].textContent.replace(/\n/g, '');
|
|
||||||
if (isNaN(newValue)) {
|
|
||||||
this.value_internal = newValue;
|
|
||||||
this.$emit('change', newValue);
|
|
||||||
} else {
|
|
||||||
this.value_internal = +newValue;
|
|
||||||
this.$emit('change', +newValue);
|
|
||||||
}
|
|
||||||
this.editing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped src="./json.scss">
|
|
||||||
</style>
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col json-level-indent">
|
|
||||||
<div class="flex flex-row" @click="expanded_internal = !expanded_internal">
|
|
||||||
<div class="item-key-line">
|
|
||||||
<template v-if="label">
|
|
||||||
<b>
|
|
||||||
<span class="item-key">"{{label}}"</span>
|
|
||||||
:
|
|
||||||
</b>
|
|
||||||
</template>
|
|
||||||
<span v-if="!expanded_internal"><b> {</b> ... <b>}</b>,</span>
|
|
||||||
<template v-else><b>{</b></template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template v-if="expanded_internal">
|
|
||||||
<div v-for="(row, rowKey) of value_internal"
|
|
||||||
:key="rowKey"
|
|
||||||
>
|
|
||||||
<template v-if="(ignoreKeys || {})[rowKey] !== true">
|
|
||||||
<JsonArray v-if="Array.isArray(row)"
|
|
||||||
:value="row"
|
|
||||||
:ignoreKeys="(ignoreKeys || {})[rowKey]"
|
|
||||||
@change="changeItem(rowKey, $event)"
|
|
||||||
>
|
|
||||||
</JsonArray>
|
|
||||||
<JsonObject v-else-if="typeof row === 'object' && row !== null"
|
|
||||||
:value="row"
|
|
||||||
:label="rowKey"
|
|
||||||
:ignoreKeys="(ignoreKeys || {})[rowKey]"
|
|
||||||
@change="changeItem(rowKey, $event)"
|
|
||||||
>
|
|
||||||
</JsonObject>
|
|
||||||
<JsonElement v-else
|
|
||||||
:value="row"
|
|
||||||
:label="rowKey"
|
|
||||||
@change="changeItem(rowKey, $event)"
|
|
||||||
>
|
|
||||||
</JsonElement>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div><b>},</b></div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import JsonArray from './JsonArray';
|
|
||||||
import JsonElement from './JsonElement';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'JsonObject',
|
|
||||||
props: [
|
|
||||||
'value',
|
|
||||||
'label',
|
|
||||||
'expanded',
|
|
||||||
'ignoreKeys',
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
JsonArray,
|
|
||||||
JsonElement,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
value_internal: undefined,
|
|
||||||
expanded_internal: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.value_internal = this.value;
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
value: function(val) {
|
|
||||||
this.value_internal = val;
|
|
||||||
},
|
|
||||||
expanded: function(val) {
|
|
||||||
if (val !== undefined && val !== null) {
|
|
||||||
this.expanded_internal = !!val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
changeItem(key, value) {
|
|
||||||
this.value_internal[key] = value;
|
|
||||||
this.$emit('change', this.value_internal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped src="./json.scss">
|
|
||||||
</style>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
.json-level-indent {
|
|
||||||
padding-left: 2em !important;
|
|
||||||
font-family: 'Overpass Mono', monospace;
|
|
||||||
}
|
|
||||||
.item-key {
|
|
||||||
color: rgb(255, 196, 148);
|
|
||||||
}
|
|
||||||
.item-key-boolean-false {
|
|
||||||
color: rgb(207, 149, 101)
|
|
||||||
}
|
|
||||||
|
|
||||||
.json-value-boolean-true {
|
|
||||||
color: rgb(150, 240, 198);
|
|
||||||
}
|
|
||||||
.json-value-boolean-false {
|
|
||||||
color: rgb(241, 21, 21);
|
|
||||||
}
|
|
||||||
.json-value-number {
|
|
||||||
color: rgb(121, 121, 238);
|
|
||||||
}
|
|
||||||
.json-value-string {
|
|
||||||
color: rgb(226, 175, 7);
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="root">
|
|
||||||
<template v-for="key in json"
|
|
||||||
>
|
|
||||||
<div class="element" :key="key">
|
|
||||||
<span class="json-key">"{{key}}"</span>:
|
|
||||||
<template v-if="Array.isArray(json[key])">
|
|
||||||
[<br/>
|
|
||||||
<div v-for="index of json[key]"
|
|
||||||
class="json-table-row"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
]
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "json-explorer",
|
|
||||||
props: {
|
|
||||||
json: Object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||