Compare commits
30 Commits
master
...
feature/op
Author | SHA1 | Date | |
---|---|---|---|
0e4b75746d | |||
41db18e1ed | |||
836bf6240e | |||
831e6b8541 | |||
81195e7b48 | |||
40e1effa2d | |||
56456bddda | |||
b1257a4c51 | |||
fb7d7735be | |||
b296552e35 | |||
689e75d3d8 | |||
557a0eef46 | |||
172f67c7ca | |||
8d6367d16c | |||
3eeef5410f | |||
6400f4cfd6 | |||
75a214962c | |||
124dc33828 | |||
45a2e5894d | |||
d238b1fffe | |||
ee57799005 | |||
43709cef34 | |||
b950fef806 | |||
96803e9b66 | |||
1f4bb40b44 | |||
02ca4bdd8e | |||
d3754e7a86 | |||
f9c86b2832 | |||
e84a1346b4 | |||
f13aca1c7f |
6
.babelrc
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@babel/plugin-proposal-class-properties"
|
"@babel/plugin-proposal-optional-chaining"
|
||||||
],
|
],
|
||||||
"presets": [
|
"presets": [
|
||||||
["@babel/preset-env", {
|
["@babel/preset-env", {
|
||||||
"useBuiltIns": false,
|
"useBuiltIns": false,
|
||||||
"targets": {
|
"targets": {
|
||||||
"esmodules": true
|
"esmodules": true,
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
6
.gitignore
vendored
@ -9,9 +9,3 @@ build/
|
|||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
*.kate-swp
|
*.kate-swp
|
||||||
|
|
||||||
src/res/img/git-ignore/
|
|
||||||
|
|
||||||
test/debug-configs/
|
|
||||||
|
|
||||||
debugging-resources/
|
|
||||||
|
23
.gitlab-ci.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
image: node:current
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- node_modules/
|
||||||
|
- .yarn
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- prep
|
||||||
|
- build
|
||||||
|
- pack
|
||||||
|
|
||||||
|
install deps:
|
||||||
|
stage: prep
|
||||||
|
script: yarn install
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script: npm run build
|
||||||
|
|
||||||
|
create zip:
|
||||||
|
stage: pack
|
||||||
|
script: npm run build-zip
|
8
.vscode/extensions.json
vendored
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"hollowtree.vue-pack",
|
|
||||||
"msjsdiag.debugger-for-chrome",
|
|
||||||
"firefox-devtools.vscode-firefox-debug",
|
|
||||||
"msjsdiag.debugger-for-edge"
|
|
||||||
]
|
|
||||||
}
|
|
31
.vscode/launch.json
vendored
@ -4,28 +4,8 @@
|
|||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
|
||||||
{
|
{
|
||||||
"type": "chrome",
|
"name": "Launch addon",
|
||||||
"request": "attach",
|
|
||||||
"name": "Attach to Chrome",
|
|
||||||
"port": 9222,
|
|
||||||
"urlFilter": "http://*/*",
|
|
||||||
"webRoot": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "firefox",
|
|
||||||
"request": "attach",
|
|
||||||
"name": "Attach (firefox)",
|
|
||||||
"pathMappings": [
|
|
||||||
{
|
|
||||||
"url": "webpack:///ext",
|
|
||||||
"path": "${workspaceFolder}/src/ext"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Launch addon (firefox)",
|
|
||||||
"type": "firefox",
|
"type": "firefox",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"port": 6000,
|
"port": 6000,
|
||||||
@ -34,9 +14,9 @@
|
|||||||
"addonPath": "${workspaceFolder}/dist-ff",
|
"addonPath": "${workspaceFolder}/dist-ff",
|
||||||
"pathMappings": [
|
"pathMappings": [
|
||||||
{
|
{
|
||||||
"url": "webpack:///ext",
|
"url": "webpack:///",
|
||||||
"path": "${workspaceFolder}/src/ext"
|
"path": "${workspaceFolder}/src/"
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -45,8 +25,5 @@
|
|||||||
"firefoxArgs": [
|
"firefoxArgs": [
|
||||||
"--start-debugger-server"
|
"--start-debugger-server"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"chrome": {
|
|
||||||
"executable": "/usr/bin/google-chrome-stable --remote-debugging-port=9222",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
18
.vscode/settings.json
vendored
@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"PILLARBOX",
|
||||||
|
"PILLARBOXED",
|
||||||
"aard",
|
"aard",
|
||||||
"ardetector",
|
"ardetector",
|
||||||
"autodetect",
|
"autodetect",
|
||||||
@ -9,45 +11,31 @@
|
|||||||
"blackbars",
|
"blackbars",
|
||||||
"blackframe",
|
"blackframe",
|
||||||
"canvas",
|
"canvas",
|
||||||
"clickthrough",
|
|
||||||
"com",
|
"com",
|
||||||
"comms",
|
"comms",
|
||||||
"csui",
|
"csui",
|
||||||
"cycleable",
|
|
||||||
"decycle",
|
"decycle",
|
||||||
"dinked",
|
|
||||||
"dinks",
|
|
||||||
"disneyplus",
|
"disneyplus",
|
||||||
"endregion",
|
|
||||||
"equalish",
|
"equalish",
|
||||||
"fith",
|
|
||||||
"fitw",
|
|
||||||
"fuckup",
|
"fuckup",
|
||||||
"gfycat",
|
"gfycat",
|
||||||
"gmail",
|
"gmail",
|
||||||
"guardline",
|
"guardline",
|
||||||
"han",
|
"han",
|
||||||
"haram",
|
|
||||||
"iframe",
|
"iframe",
|
||||||
"imgur",
|
"imgur",
|
||||||
"insta",
|
"insta",
|
||||||
"letterboxed",
|
"letterboxed",
|
||||||
"manjaro",
|
"manjaro",
|
||||||
"mdicon",
|
|
||||||
"mdijs",
|
|
||||||
"minification",
|
"minification",
|
||||||
"mitigations",
|
|
||||||
"nogrow",
|
"nogrow",
|
||||||
"noshrink",
|
"noshrink",
|
||||||
"outro",
|
"outro",
|
||||||
"PILLARBOX",
|
|
||||||
"PILLARBOXED",
|
|
||||||
"polyfill",
|
"polyfill",
|
||||||
"recursing",
|
"recursing",
|
||||||
"reddit",
|
"reddit",
|
||||||
"rescan",
|
"rescan",
|
||||||
"resizer",
|
"resizer",
|
||||||
"resizers",
|
|
||||||
"scrollbar",
|
"scrollbar",
|
||||||
"shitiness",
|
"shitiness",
|
||||||
"smallcaps",
|
"smallcaps",
|
||||||
@ -55,8 +43,8 @@
|
|||||||
"tabitem",
|
"tabitem",
|
||||||
"tablist",
|
"tablist",
|
||||||
"tamius",
|
"tamius",
|
||||||
|
"stdev",
|
||||||
"textbox",
|
"textbox",
|
||||||
"ultrawide",
|
|
||||||
"ultrawidify",
|
"ultrawidify",
|
||||||
"unmark",
|
"unmark",
|
||||||
"unmarking",
|
"unmarking",
|
||||||
|
221
CHANGELOG.md
@ -1,222 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v6.0 (current major)
|
## v4.x
|
||||||
|
|
||||||
### v6.3.0
|
### Plans for the future
|
||||||
* 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 detects embedded content (but not always)
|
|
||||||
* Added `www.youtube-nocookie.com` to "officially supported" list
|
|
||||||
|
|
||||||
### v6.2.5
|
* WebGL
|
||||||
|
* Native builds for Chromium Edge and Opera
|
||||||
|
* Settings page looks ugly af right now. Maybe fix it some time later
|
||||||
|
* other bug fixes
|
||||||
|
|
||||||
* Popup appearance changed — UI advertisement panel was moved to the popup header
|
QoL improvements for me:
|
||||||
* 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
|
* logging: allow to enable logging at will and export said logs to a file
|
||||||
* [#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
|
### v4.4.8 (Current)
|
||||||
* 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
|
|
||||||
|
|
||||||
* Fixed external links
|
|
||||||
|
|
||||||
### v6.0.0
|
|
||||||
|
|
||||||
Chrome only, because I needed to rush manifest v3 migration before ensuring things work in Firefox.
|
|
||||||
|
|
||||||
* In player UI
|
|
||||||
* New alignment options (can align video both vertically, as well as horizontally)
|
|
||||||
* Extension does a little bit of a better job differentiating between levels of support for a given site
|
|
||||||
* Changed how cropping and panning works. This should lead to fewer problems and better generic support.
|
|
||||||
* REGRESSION: no manual panning with shift + mouse movement
|
|
||||||
* "Player select" screen, which provides a GUI way for picking HTML element that acts as the video player area
|
|
||||||
|
|
||||||
Regressions and potential issues:
|
|
||||||
* Settings were largely not migrated from previous versions. This is as intended, since changes to cropping (and some other aspects)
|
|
||||||
rendered certain old settings obsolete.
|
|
||||||
* Extension can no longer discriminate between iframes — extension popup commands get sent to ALL iframes on page
|
|
||||||
* Extension probably can't discriminate between multiple videos on a single page
|
|
||||||
|
|
||||||
## v5.x
|
|
||||||
|
|
||||||
### v5.1.7
|
|
||||||
|
|
||||||
Firefox-only.
|
|
||||||
|
|
||||||
* When cropping and panning video, CSS' `transform: translate(x,y)` now uses integers _always_. Before that fix, `translate(x,y)` could be offset by x.5 px, and that half of a pixel introduced a 1px thick line on the portion of the left edge of the video.
|
|
||||||
|
|
||||||
### v5.1.7
|
|
||||||
|
|
||||||
* When aligning videos, ensure video is never translated by a fractional value
|
|
||||||
* As of recent nVidia driver update in Edge, Angle detection fails in a way that prevents popup from showing up. This problem should be fixed now.
|
|
||||||
|
|
||||||
The angle detection problem was fixed by disabling the angle detection check for now, as Chrome, et al. appear to have fixed buggy video hardware acceleration.
|
|
||||||
|
|
||||||
### v5.1.6
|
|
||||||
|
|
||||||
* Added new config for disney+ (courtesy of @MStefan99)
|
|
||||||
* Chrome hardware acceleration is bugged and cannot be worked around in-extension. Only way to fix this is for users to change their Google Chrome settings. Added naggathon with instructions into the extension popup.
|
|
||||||
|
|
||||||
### v5.1.5
|
|
||||||
|
|
||||||
* Fixed laginess in Chromium-based browsers on Windows. Details in [#199](https://github.com/tamius-han/ultrawidify/issues/199#issuecomment-1221383134)
|
|
||||||
|
|
||||||
### v5.1.4
|
|
||||||
|
|
||||||
* Fixed some problems with autodetection not returning to 16:9 when necessary if autodetection already changed aspect ratio ([#198](https://github.com/tamius-han/ultrawidify/issues/198))
|
|
||||||
|
|
||||||
### v5.1.3
|
|
||||||
|
|
||||||
* Fixed some problems with autodetection sometimes briefly resetting on dark frames ([#195](https://github.com/tamius-han/ultrawidify/issues/195), [#196](https://github.com/tamius-han/ultrawidify/issues/196))
|
|
||||||
|
|
||||||
### v5.1.2
|
|
||||||
|
|
||||||
* `set-extension-mode` turned into `set-ExtensionMode` at some point. This caused in "Enable this extension" options to vanish on certain setups.
|
|
||||||
* Blackframe tests now run on same data as main algorithm as opposed on a smaller sample (`drawImage()` calls are _very_ expensive even for a 16x9 sample).
|
|
||||||
|
|
||||||
### v5.1.1
|
|
||||||
|
|
||||||
* Fixed autodetection
|
|
||||||
|
|
||||||
### v5.1.0
|
|
||||||
|
|
||||||
* Re-enable logger
|
|
||||||
* Move aspect ratio autodetection to requestAnimationFrame
|
|
||||||
* Fix netflix
|
|
||||||
|
|
||||||
### v5.0.7
|
|
||||||
|
|
||||||
* Videos of square-ish aspect ratios on 1440p (and lower) resolutions now no longer get misaligned ([#162](https://github.com/tamius-han/ultrawidify/issues/162))
|
|
||||||
* Alignment of featured videos on youtube channel page should now also be fixed
|
|
||||||
|
|
||||||
### v5.0.6
|
|
||||||
* Added configuration for metaivi.com based on user feedback ([#160](https://github.com/tamius-han/ultrawidify/issues/160))
|
|
||||||
* Removed ExtConfPatches for versions < 4.5.0, because nobody should be using a build of this extension that's over a year old
|
|
||||||
|
|
||||||
### v5.0.5
|
|
||||||
|
|
||||||
* improved UX a bit
|
|
||||||
* Fixed white background on app.plex.tv ([#158](https://github.com/tamius-han/ultrawidify/issues/158))
|
|
||||||
|
|
||||||
### v5.0.4
|
|
||||||
* Attempt to fix disney+ again, courtesy of [@jwannebo](https://github.com/tamius-han/ultrawidify/issues/84#issuecomment-846334005) on github.
|
|
||||||
|
|
||||||
|
|
||||||
### v5.0.3
|
|
||||||
|
|
||||||
* Fixed the issue where the videos were sometimes offset up and left. Again.
|
|
||||||
* Fix the issue where correcting source stretch was squished incorrectly ([#153](https://github.com/tamius-han/ultrawidify/issues/153))
|
|
||||||
|
|
||||||
### v5.0.2
|
|
||||||
|
|
||||||
* When in full screen, the extension will assume player element dimensions are the same as the screen resolution. This should help with sites where ultrawidify doesn't correctly identify the player, as cropping generally doesn't work if player element is not identified. Old behaviour can be restored in advanced extension settings by toggling the "use player aspect ratio in fullscreen" checkbox under 'player detection settings'.
|
|
||||||
* Extension should now respect 'disable extension' option for real.
|
|
||||||
* Fixed the issue where player wouldn't get detected if video was wider than the player.
|
|
||||||
|
|
||||||
|
|
||||||
### v5.0.1
|
|
||||||
* Added an option for users to turn off (and/or configure) Chrome/Edge's zoom limiter.
|
|
||||||
|
|
||||||
### v5.0.0
|
|
||||||
|
|
||||||
There's been some big-ish changes under the hood:
|
|
||||||
|
|
||||||
* Migrate main scripts to typescript (vue is currently not included).
|
|
||||||
* webextension-polyfill is now used everywhere (if only because typescript throws a hissy fit with `browser` and `chrome` otherwise) ([#114](https://github.com/tamius-han/ultrawidify/issues/114))
|
|
||||||
* Fix some bugs that I didn't even know I had, but typescript kinda shone some light on them
|
|
||||||
* Manual zoom (Z/U unless sites override the two) should now work again (without automatic AR constantly overriding it). Same goes for panning. ([#135](https://github.com/tamius-han/ultrawidify/issues/135) & [#138](https://github.com/tamius-han/ultrawidify/issues/138))
|
|
||||||
* Fix issue when video would be scaled incorrectly if video element uses `height:auto`.
|
|
||||||
* **[5.0.0.1]** Fixed the issue where settings were reset on page load.
|
|
||||||
* **[5.0.0.1]** Fixed the issue where settings page wouldn't load.
|
|
||||||
## v4.x (current major)
|
|
||||||
|
|
||||||
### v4.5.3
|
|
||||||
|
|
||||||
* Provides workaround for the fullscreen stretching bug Chrome 88 (or a recent Windows 10 update) introduced for nVidia users using hardware acceleration on Windows 10. In order to mitigate this bug, Ultrawidify needs to keep a 5-10 px wide black border while watching videos in full screen. This bug is also present in Edge.
|
|
||||||
* **[4.5.3.1]** Fixed letterbox misalignment binding in settings (#134)
|
|
||||||
* **[4.5.3.2]** Fixed false 'autodetection not supported' notifications.
|
|
||||||
|
|
||||||
### v4.5.2
|
|
||||||
|
|
||||||
* Fixed the issue where videos would sometimes get misaligned while using hybrid stretch, except for real this time. ([#125](https://github.com/tamius-han/ultrawidify/issues/125))
|
|
||||||
* Improved DRM detection (the 'autodetection cannot work on this site' popup should now no longer show up on the sites where autodetection _can_ work)
|
|
||||||
|
|
||||||
### v4.5.1
|
|
||||||
|
|
||||||
* Fixed the misalignment issue on netflix ... hopefully.
|
|
||||||
* 'Site settings' tab should now work in Chrome as well ([#126](https://github.com/tamius-han/ultrawidify/issues/126))
|
|
||||||
* Popup interface now refreshes properly ([#127](https://github.com/tamius-han/ultrawidify/issues/127))
|
|
||||||
* Videos should now be scaled correctly when the display is narrower than video's native aspect ratio ([#118](https://github.com/tamius-han/ultrawidify/issues/118))
|
|
||||||
* Fullscreen videos on streamable are aligned correctly ([#116](https://github.com/tamius-han/ultrawidify/issues/118)).
|
|
||||||
* **[4.5.1.1]** Streamable fix broke old.reddit + RES on embeds from v.redd.it and streamable.com. We're now using an alternative implementation. ([#128](https://github.com/tamius-han/ultrawidify/issues/128))
|
|
||||||
* **[4.5.1.2]** Fixed the issue where videos would sometimes get misaligned while using hybrid stretch. ([#125](https://github.com/tamius-han/ultrawidify/issues/125))
|
|
||||||
* **[4.5.1.3]** Added fix for disney plus
|
|
||||||
* **[4.5.1.3]** Microsoft Edge has fixed the bugs that prevented the extension from working properly. Popup should no longer be shown.
|
|
||||||
|
|
||||||
|
|
||||||
### v4.5.0 (Current)
|
|
||||||
|
|
||||||
* Under the hood: migrated from vue2 to vue3, because optional chaining in templates is too OP.
|
|
||||||
* (On options page, section 'Action & shortcuts') Manual aspect ratio now supports entering custom ratios using '21/9' and '2.39:1' formats (as opposed to single number, e.g. '2.39') — [#121](https://github.com/tamius-han/ultrawidify/issues/121).
|
|
||||||
* Added config for wakanim.tv (special thanks to @saschanaz for doing the legwork — [#113](https://github.com/tamius-han/ultrawidify/issues/113))
|
|
||||||
* (In Firefox) When extension was placed in overflow menu, the popup was cut off. That should be fixed now. [#119](https://github.com/tamius-han/ultrawidify/issues/119)
|
|
||||||
* The extension will now show a notification when autodetection can't run due to DRM
|
|
||||||
* Videos on facebook and reddit no longer get shifted up and to the left for me (cropping most of the video off-screen), but I haven't been deliberately trying to fix that issue. If you experience that issue, please consider contacting me (via github or email) with a link to a problematic video.
|
|
||||||
|
|
||||||
### v4.4.10
|
|
||||||
|
|
||||||
* Video alignment should now work on Twitch — [#109](https://github.com/tamius-han/ultrawidify/issues/109)
|
|
||||||
* Videos should now align properly on Hulu while cropped — [#111](https://github.com/tamius-han/ultrawidify/issues/111) & via email
|
|
||||||
* Fixed a problem where changing certain settings would cause multiple instances of Ultrawidify to run on a page, effectively preventing some crop options to be set until reload. (possibly [#112](https://github.com/tamius-han/ultrawidify/issues/112)?)
|
|
||||||
* Fixed a problem where embedded videos would be misaligned after switching from full screen
|
|
||||||
* **[4.4.10.1]** Fixed cruncyhroll regression — [#109](https://github.com/tamius-han/ultrawidify/issues/115)
|
|
||||||
|
|
||||||
### v4.4.9
|
|
||||||
|
|
||||||
* Fixed the youtube alignment issue (previously fixed in v4.4.7.1-2), but this time for real (and in a bit more proper way)
|
|
||||||
* Fixed the bug where extension wouldn't work when URL specified a port (e.g. www.example.com:80)
|
|
||||||
* **[4.4.9.1]** removed source files from extension build in order to decrease package size
|
|
||||||
* **[4.4.9.2]** updated dependencies and stuff
|
|
||||||
|
|
||||||
In addition to that, as of 4.4.9.1 the build process ensures removal of `node_modules` before building the extension so we can have reproducible builds except for real this time. Hopefully.
|
|
||||||
|
|
||||||
### v4.4.8
|
|
||||||
|
|
||||||
* Fixed the bug where on pages with more than one video, the list of available videos in the extension popup wouldn't remove videos that are no longer displayed on site. This resulted in extension listing videos that were no longer on the page. Reboot or navigation would also not clear the list if navigating between various pages on the same host.
|
* Fixed the bug where on pages with more than one video, the list of available videos in the extension popup wouldn't remove videos that are no longer displayed on site. This resulted in extension listing videos that were no longer on the page. Reboot or navigation would also not clear the list if navigating between various pages on the same host.
|
||||||
* Fixed the chrome-only bug where on sites with more than one video, the number wouldn't get hidden when the extension popup closed.
|
* Fixed the chrome-only bug where on sites with more than one video, the number wouldn't get hidden when the extension popup closed.
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
# 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.
|
|
@ -1,6 +0,0 @@
|
|||||||
# Implementation details
|
|
||||||
|
|
||||||
## Enabling/disabling aspect ratio corrections
|
|
||||||
|
|
||||||
* Aspect ratios are changed by proxy. Extension attaches **a custom CSS class** to `video` and `player` elements.
|
|
||||||
* To prevent extension from affecting the appearance of a webpage, **it's sufficient to remove our custom CSS classes from `video` and `player` elements.**
|
|
32
Jenkinsfile
vendored
@ -1,32 +0,0 @@
|
|||||||
// required jenkins plugins:
|
|
||||||
// * https://plugins.jenkins.io/git/
|
|
||||||
|
|
||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
|
|
||||||
stages {
|
|
||||||
|
|
||||||
// stage('Check for changes') {
|
|
||||||
// sh "env.GIT_COMMIT != env.GIT_PREVIOUS_COMMIT"
|
|
||||||
// }
|
|
||||||
|
|
||||||
stage('Install dependencies') {
|
|
||||||
steps {
|
|
||||||
sh 'npm ci'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Build') {
|
|
||||||
steps {
|
|
||||||
sh 'npm run build-all'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Push to release server') {
|
|
||||||
steps {
|
|
||||||
sh "echo 'implement me pls!'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
35
LICENSE.MD
@ -1,35 +0,0 @@
|
|||||||
# 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.
|
|
@ -4,28 +4,14 @@
|
|||||||
|
|
||||||
The extension is built on a PC running Manjaro Linux. npm and node are installed from repositories/aur.
|
The extension is built on a PC running Manjaro Linux. npm and node are installed from repositories/aur.
|
||||||
|
|
||||||
### Software versions:
|
|
||||||
|
|
||||||
Node/npm versions:
|
## Installing dependencies
|
||||||
|
|
||||||
```
|
Run `npm ci`
|
||||||
node: %%NODE_VERSION%%
|
|
||||||
npm: %%NPM_VERSION%%
|
|
||||||
```
|
|
||||||
|
|
||||||
Linux (`uname -a`):
|
|
||||||
|
|
||||||
```
|
|
||||||
%%LINUX_VERSION%%
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reproducing build
|
## Reproducing build
|
||||||
|
|
||||||
Run the following commands to install dependencies and compile the firefox build:
|
`npm run build`
|
||||||
|
|
||||||
```
|
The compiled code pops up in /dist-ff (/dist-chrome for Chromium-based browsers).
|
||||||
npm ci
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
The compiled code pops up in `/dist-ff`.
|
|
52
README.md
@ -1,8 +1,8 @@
|
|||||||
# Ultrawidify — aspect ratio fixer for youtube and netflix
|
# Ultrawidify — aspect ratio fixer for youtube and netflix
|
||||||
|
|
||||||
## Super TL;DR: I'm just looking for the install links, thanks
|
## Super TL;DR: I'm just looking for the install links, thanks
|
||||||
|
|
||||||
[Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/), [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi), [Edge](https://microsoftedge.microsoft.com/addons/detail/ultrawidify/lmpgpgechmkkkehkihpiddbcbgibokbi).
|
[Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/), [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi), [Edge](https://github.com/tamius-han/ultrawidify#microsoft-edge) (Chromium-based only)
|
||||||
|
|
||||||
There's also [nightly "builds"](https://stuff.lionsarch.tamius.net/ultrawidify/nightly/).
|
There's also [nightly "builds"](https://stuff.lionsarch.tamius.net/ultrawidify/nightly/).
|
||||||
|
|
||||||
@ -13,9 +13,10 @@ If you own an ultrawide monitor, you have probably noticed that sometimes videos
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Known issues
|
## Known issues
|
||||||
|
|
||||||
* Netflix autodetection not working in Chrome and working poorly in Firefox. This problem happens because DRM, and happens on other sites utilizing DRM protection schemes. Don't expect Chrome support any time soon.
|
* Netflix autodetection not working in Chrome, wontfix as issue is fundamentally unfixable.
|
||||||
* Everything reported in [issues](https://github.com/tamius-han/ultrawidify/issues)
|
* Everything reported in [issues](https://github.com/tamius-han/ultrawidify/issues)
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
@ -24,14 +25,15 @@ If you own an ultrawide monitor, you have probably noticed that sometimes videos
|
|||||||
* Autodetection is only correct 95% of the time, most of the time.
|
* Autodetection is only correct 95% of the time, most of the time.
|
||||||
* That new stretching mode wasn't thoroughly tested yet. Issues may be present. (Same with zoom)
|
* That new stretching mode wasn't thoroughly tested yet. Issues may be present. (Same with zoom)
|
||||||
* Enabling extension everywhere (as opposed to whitelisted sites) could break some websites.
|
* Enabling extension everywhere (as opposed to whitelisted sites) could break some websites.
|
||||||
|
* Edge has
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **Can be enabled or disabled on per-site basis**
|
* **Can be enabled or disabled on per-site basis**
|
||||||
* **Crop video to fit screen** (no stretching. Supported aspect ratios: 21/9 (1:2.39), 16:9, 16:10. It's possible to set additional aspect ratios, but settings GUI currently contains some mildly annoying bugs)
|
* **Crop video to fit screen** (no stretching. Supported aspect ratios: 21/9 (1:2.39), 16:9, 16:10, _one (1) custom aspect ratio_)
|
||||||
* **Automatic aspect ratio detection** (can be enabled/disabled entirely or on a per-site basis, separately of the extension. May not work on sites utilizing DRM schemes, such as Netflix et. al.). Autodetection in action: [youtube](https://www.youtube.com/watch?v=j2xn1WpbtCQ))
|
* **Automatic aspect ratio detection** (can be enabled/disabled entirely or on a per-site basis, separately of the extension. Autodetection in action: [youtube](https://www.youtube.com/watch?v=j2xn1WpbtCQ))
|
||||||
* **Supports Youtube theater mode**
|
* **Supports Youtube theater mode**
|
||||||
* **[EXPERIMENTAL!]** Stretch video to fit the screen
|
* **[EXPERIMENTAL!]** Stretch video to fit the screen (4 different approaches)
|
||||||
* **[EXPERIMENTAL!]** custom zooming and panning
|
* **[EXPERIMENTAL!]** custom zooming and panning
|
||||||
|
|
||||||
|
|
||||||
@ -39,23 +41,19 @@ If you own an ultrawide monitor, you have probably noticed that sometimes videos
|
|||||||
|
|
||||||
* Youtube
|
* Youtube
|
||||||
* Netflix
|
* Netflix
|
||||||
* Twitch
|
|
||||||
|
|
||||||
### Other sites
|
### Other sites
|
||||||
|
|
||||||
I am not actively testing extension on other sites. You can try your luck and enable extension for any unsupported site you stumble across via extension popup, but I make no guarantees it will work everywhere.
|
I am not actively testing extension on other sites. You can try your luck and enable extension for any unsupported site you stumble across via extension popup, but I make no guarantees it will work everywhere.
|
||||||
|
|
||||||
If extension doesn't work for a site I'm not testing on out of the box, follow [this wiki](https://github.com/tamius-han/ultrawidify/wiki/Fixing-site-incompatibilites-('Advanced-settings')). The 'quick and dirty' approach should work for most sites. (If you try doing things the proper way, you should really know what you're doing.)
|
|
||||||
|
|
||||||
### Installing this extension
|
### Installing this extension
|
||||||
|
|
||||||
You can download this extension from the relevant extension stores:
|
You can download this extension from Firefox' and Chrome's extension stores:
|
||||||
|
|
||||||
* [Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/)
|
* [Firefox](https://addons.mozilla.org/en/firefox/addon/ultrawidify/)
|
||||||
* [Chrome](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
|
* [Chrome, Opera, Chromium Edge](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
|
||||||
* [Edge](https://microsoftedge.microsoft.com/addons/detail/ultrawidify/lmpgpgechmkkkehkihpiddbcbgibokbi)
|
|
||||||
|
|
||||||
Other browsers are not officially supported. If you're using a different Chromium-based browser, you can try installing the addon from the Chrome Web Store — but if things don't work, you're on your own.
|
Opera users and users of the new, Chromium-based Edge can install Ultrawidify from Chrome Web Store as well.
|
||||||
|
|
||||||
### Nightly builds
|
### Nightly builds
|
||||||
|
|
||||||
@ -82,7 +80,7 @@ You can make a donation [via Paypal](https://www.paypal.me/tamius).
|
|||||||
|
|
||||||
# The long version
|
# The long version
|
||||||
|
|
||||||
The technology has been here for a while, but plenty of people don't know how to properly encode a video (despite the fact [youtube has an article that explains aspect ratios](https://support.google.com/youtube/answer/6375112)). Plenty of people surprisingly includes major Hollywood studios, such as [Marvel](https://www.youtube.com/watch?v=Ke1Y3P9D0Bc), [Disney](https://www.youtube.com/watch?v=yCOPJi0Urq4), [Dreamworks](https://www.youtube.com/watch?v=oKiYuIsPxYk), [Warner Brothers](https://www.youtube.com/watch?v=VYZ3U1inHA4), [Sony](https://www.youtube.com/watch?v=7BWWWQzTpNU), et cetera. You'd think that this is the one thing Hollywood studios and people who make [music videos for a living](https://www.youtube.com/watch?v=c6Mx2mxpaCY) would know how to do right, but they don't. This extension is here to fix that.
|
The technology has been here for a while, but plenty of people don't know how to properly encode a video (despite the fact [youtube has an article that explains aspect ratios](https://support.google.com/youtube/answer/6375112)). Plenty of people surprisingly includes major Holywood studios, such as [Marvel](https://www.youtube.com/watch?v=Ke1Y3P9D0Bc), [Disney](https://www.youtube.com/watch?v=yCOPJi0Urq4), [Dreamworks](https://www.youtube.com/watch?v=oKiYuIsPxYk), [Warner Brothers](https://www.youtube.com/watch?v=VYZ3U1inHA4), [Sony](https://www.youtube.com/watch?v=7BWWWQzTpNU), et cetera. You'd think that this is the one thing Holywood studios and people who make [music videos for a living](https://www.youtube.com/watch?v=c6Mx2mxpaCY) would know how to do right, but they don't. This extension is here to fix that.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -235,6 +233,7 @@ However, I do plan on implementing this feature. Hopefully by the end of the yea
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Plans for the future
|
## Plans for the future
|
||||||
|
|
||||||
1. Handle porting of extension settings between versions.
|
1. Handle porting of extension settings between versions.
|
||||||
@ -253,9 +252,6 @@ However, I do plan on implementing this feature. Hopefully by the end of the yea
|
|||||||
|
|
||||||
[Latest stable for Chrome — download from Chrome store](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
|
[Latest stable for Chrome — download from Chrome store](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
|
||||||
|
|
||||||
Edge version is currently not available, as Edge has some bugs that prevent this extension from working correctly. [Read more](https://github.com/tamius-han/ultrawidify/issues/117#issuecomment-747109695)
|
|
||||||
<!-- [Latest stable for Edge — Download from Microsoft store](https://microsoftedge.microsoft.com/addons/detail/lmpgpgechmkkkehkihpiddbcbgibokbi) -->
|
|
||||||
|
|
||||||
### Installing the current, github version
|
### Installing the current, github version
|
||||||
|
|
||||||
## Get pre-built version:
|
## Get pre-built version:
|
||||||
@ -268,7 +264,7 @@ Requirements: npm, node.
|
|||||||
|
|
||||||
1. Clone this repo
|
1. Clone this repo
|
||||||
2. run `npm install`
|
2. run `npm install`
|
||||||
3. If using **Firefox,** run: `npm run watch:dev`. If using **Chrome,** run: `npm run watch-chrome:dev`.
|
3. If using **Firefox,** run: `npm run watch:dev`. If using **Chrome,** run: `npm run watch-chrome:dev`. If using Edge, run: `npm run watch-edge:dev`.
|
||||||
|
|
||||||
TODO: see if #3 already loads the extension in FF
|
TODO: see if #3 already loads the extension in FF
|
||||||
|
|
||||||
@ -277,6 +273,26 @@ TODO: see if #3 already loads the extension in FF
|
|||||||
4. Add temporary addon
|
4. Add temporary addon
|
||||||
5. Select `${ultrawidify_folder}/dist/manifest.json`
|
5. Select `${ultrawidify_folder}/dist/manifest.json`
|
||||||
|
|
||||||
|
|
||||||
|
# Microsoft Edge
|
||||||
|
|
||||||
|
With the advent of the new Chromium-based Edge, this extension should work just fine. I don't actively test in Edge, though, so your mileage may vary.
|
||||||
|
|
||||||
|
## Chromium-based
|
||||||
|
|
||||||
|
1. Visit [edge://extensions](edge://extensions/)
|
||||||
|
2. Go to [Chrome Web Store](https://chrome.google.com/webstore/detail/ultrawidify/dndehlekllfkaijdlokmmicgnlanfjbi)
|
||||||
|
3. Click 'Allow extensions from other stores' on the blue popup bar at the top of the screen
|
||||||
|
4. Install Ultrawidify
|
||||||
|
5. Enjoy
|
||||||
|
|
||||||
|
I might reconsider publishing extension for Chromium-based Microsoft Edge once it's released. Releasing in MS Store appears to be impossible at current time as extension submissions don't appear to be open at all (unless you got a special invite or something).
|
||||||
|
|
||||||
|
## Old Edge
|
||||||
|
|
||||||
|
1. Get [Chromium-based Edge](https://www.microsoftedgeinsider.com/en-us/)
|
||||||
|
2. See steps above
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
see changelog.md
|
see changelog.md
|
||||||
|
13558
package-lock.json
generated
85
package.json
@ -1,79 +1,54 @@
|
|||||||
{
|
{
|
||||||
"name": "ultrawidify",
|
"name": "ultrawidify",
|
||||||
"version": "6.3.0",
|
"version": "4.4.8",
|
||||||
"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-all": "bash ./scripts/build-all.sh",
|
|
||||||
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack --hide-modules",
|
|
||||||
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack --hide-modules",
|
|
||||||
"build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack --hide-modules",
|
|
||||||
"build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack --hide-modules",
|
|
||||||
"build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack --hide-modules",
|
|
||||||
"build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack --hide-modules",
|
|
||||||
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack --hide-modules",
|
|
||||||
"build-zip": "node scripts/build-zip.js",
|
|
||||||
"build:dev": "webpack --hide-modules",
|
|
||||||
"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-ff": "rm -rf ./dist-ff && cross-env NODE_ENV=development CHANNEL=dev BROWSER=firefox npm run build:dev -- --watch",
|
|
||||||
"dev-chrome": "rm -rf ./dist-chrome && cross-env NODE_ENV=development CHANNEL=dev BROWSER=chrome npm run build:dev -- --watch",
|
|
||||||
"dev-edge": "rm -rf ./dist-edge && cross-env NODE_ENV=development CHANNEL=dev BROWSER=edge npm run build:dev -- --watch",
|
|
||||||
"pre-build": "rm -rf ./dist-ff; rm -rf ./dist-chrome; rm -rf ./dist-edge; rm -rf ./node_modules; npm ci",
|
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
"start:windows": "npm run dev:windows"
|
"build": "cross-env NODE_ENV=production BROWSER=firefox CHANNEL=stable webpack --hide-modules",
|
||||||
|
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack --hide-modules",
|
||||||
|
"build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack --hide-modules",
|
||||||
|
"build:dev": "webpack --hide-modules",
|
||||||
|
"build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack --hide-modules",
|
||||||
|
"build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack --hide-modules",
|
||||||
|
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack --hide-modules",
|
||||||
|
"build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack --hide-modules",
|
||||||
|
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack --hide-modules",
|
||||||
|
"build-all": "mkdir -p ./build/old; rm -rf ./dist-ff; rm -rf ./dist-chrome; rm ./dist-zip/uw-amo-source.zip; mv -f ./dist-zip/*.zip ./build/old; npm run build; node scripts/build-zip.js ff; npm run build-chrome; node scripts/build-zip.js chrome; ./scripts/prepare-amo-source.sh",
|
||||||
|
"build-zip": "node scripts/build-zip.js",
|
||||||
|
"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\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
"@types/core-js": "^2.5.0",
|
||||||
"@mdi/font": "^6.5.95",
|
"@types/es6-promise": "^3.3.0",
|
||||||
"@mdi/js": "^6.4.95",
|
"concurrently": "^5.1.0",
|
||||||
"@types/resize-observer-browser": "^0.1.6",
|
|
||||||
"concurrently": "^5.3.0",
|
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
"gl-matrix": "^3.4.3",
|
|
||||||
"json-cyclic": "0.0.3",
|
"json-cyclic": "0.0.3",
|
||||||
"lodash": "^4.17.21",
|
"vue": "^2.6.11",
|
||||||
"mdi-vue": "^3.0.11",
|
"vuex": "^3.1.2",
|
||||||
"typescript": "^4.4.4",
|
"vuex-webextensions": "^1.3.0",
|
||||||
"vanilla-jsoneditor": "^3.3.0",
|
"webextension-polyfill": "^0.6.0"
|
||||||
"vue": "^3.2.21",
|
|
||||||
"vue-style-loader": "^4.1.3",
|
|
||||||
"vuex": "^4.0.2",
|
|
||||||
"vuex-webextensions": "^1.3.3",
|
|
||||||
"webextension-polyfill": "^0.12.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@babel/core": "^7.8.3",
|
||||||
"@babel/preset-env": "^7.16.0",
|
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
||||||
"@types/chrome": "0.0.240",
|
"@babel/preset-env": "^7.8.3",
|
||||||
"@types/core-js": "^2.5.5",
|
|
||||||
"@types/es6-promise": "^3.3.0",
|
|
||||||
"@types/firefox": "0.0.31",
|
|
||||||
"@types/lodash": "^4.14.176",
|
|
||||||
"@types/node": "^14.17.32",
|
|
||||||
"@vue/cli": "^4.5.15",
|
|
||||||
"@vue/cli-plugin-typescript": "^4.5.15",
|
|
||||||
"@vue/compiler-sfc": "^3.2.21",
|
|
||||||
"archiver": "^3.0.0",
|
"archiver": "^3.0.0",
|
||||||
"babel-loader": "^8.2.3",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-preset-es2020": "^1.0.2",
|
|
||||||
"copy-webpack-plugin": "^4.5.3",
|
"copy-webpack-plugin": "^4.5.3",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"ejs": "^2.7.4",
|
"ejs": "^2.7.4",
|
||||||
"file-loader": "^1.1.11",
|
"file-loader": "^1.1.11",
|
||||||
"mini-css-extract-plugin": "^0.4.4",
|
"mini-css-extract-plugin": "^0.4.4",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.13.1",
|
||||||
"resolve-url-loader": "^5.0.0",
|
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"ts-loader": "^8.3.0",
|
"vue-loader": "^15.8.3",
|
||||||
"vue-cli-plugin-vue-next": "~0.1.4",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"vue-loader": "^16.8.2",
|
"web-ext-types": "^2.1.0",
|
||||||
"web-ext-types": "^2.3.0",
|
"webpack": "^4.41.5",
|
||||||
"webpack": "^4.46.0",
|
|
||||||
"webpack-chrome-extension-reloader": "^0.8.3",
|
"webpack-chrome-extension-reloader": "^0.8.3",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^3.3.10",
|
||||||
"webpack-shell-plugin": "^0.5.0"
|
"webpack-shell-plugin": "^0.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
releases/edge/ultrawidify_edge_v3.2.2.zip
Normal file
@ -1,42 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# NOTE: this script needs to be run with the npm run build-all
|
|
||||||
# command from the root directory of the project. Running it in
|
|
||||||
# any other way probably isn't going to work.
|
|
||||||
|
|
||||||
# pre-build steps:
|
|
||||||
mkdir -p ./build/old
|
|
||||||
npm run pre-build
|
|
||||||
rm ./dist-zip/uw-amo-source.zip
|
|
||||||
mv -f ./dist-zip/*.zip ./build/old
|
|
||||||
|
|
||||||
# lets force raise ram limit, but the improper way
|
|
||||||
# export NODE_OPTIONS=--max_old_space_size=4096
|
|
||||||
|
|
||||||
# build the version for each browser and create a zip afterwards
|
|
||||||
# step 1: define build functions
|
|
||||||
#function buildFF {
|
|
||||||
npm run build
|
|
||||||
node scripts/build-zip.js ff
|
|
||||||
#}
|
|
||||||
#function buildChrome {
|
|
||||||
npm run build-chrome
|
|
||||||
node scripts/build-zip.js chrome
|
|
||||||
#}
|
|
||||||
#function buildEdge {
|
|
||||||
npm run build-edge
|
|
||||||
node scripts/build-zip.js edge
|
|
||||||
#}
|
|
||||||
|
|
||||||
# step 2: execute them all at once
|
|
||||||
# buildFF &
|
|
||||||
# buildChrome &
|
|
||||||
# buildEdge &
|
|
||||||
|
|
||||||
# wait < <(jobs -p)
|
|
||||||
|
|
||||||
# prepare AMO source
|
|
||||||
# source code needs to be prepared AFTER
|
|
||||||
# the code has been built, to ensure that
|
|
||||||
# package-lock.json remains unchanged
|
|
||||||
./scripts/prepare-amo-source.sh
|
|
@ -37,9 +37,6 @@ if [ ! -z "$GIT_COMMIT" ] ; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# let's raise RAM limit for npm command globally
|
|
||||||
NODE_OPTIONS=--max_old_space_size=4096
|
|
||||||
|
|
||||||
npm ci
|
npm ci
|
||||||
|
|
||||||
rm -rf ./dist-zip || true # no big deal if ./dist-zip doesn't exist
|
rm -rf ./dist-zip || true # no big deal if ./dist-zip doesn't exist
|
||||||
@ -49,7 +46,7 @@ mkdir dist-zip # create it back
|
|||||||
# build firefox
|
# build firefox
|
||||||
#
|
#
|
||||||
npm run "${BUILD_SCRIPT}"
|
npm run "${BUILD_SCRIPT}"
|
||||||
node --max-old-space-size=2048 scripts/build-zip.js ff nightly
|
node scripts/build-zip.js ff nightly
|
||||||
# if [ ! -z "${AMO_API_KEY}" ] ; then
|
# if [ ! -z "${AMO_API_KEY}" ] ; then
|
||||||
# if [ ! -z "${AMO_API_SECRET}" ] ; then
|
# if [ ! -z "${AMO_API_SECRET}" ] ; then
|
||||||
# web-ext sign --source-dir ./dist --api-key "${AMO_API_KEY}" --api-secret "${AMO_API_SECRET}"
|
# web-ext sign --source-dir ./dist --api-key "${AMO_API_KEY}" --api-secret "${AMO_API_SECRET}"
|
||||||
@ -60,18 +57,12 @@ node --max-old-space-size=2048 scripts/build-zip.js ff nightly
|
|||||||
# build chrome
|
# build chrome
|
||||||
#
|
#
|
||||||
npm run "${BUILD_SCRIPT}-chrome"
|
npm run "${BUILD_SCRIPT}-chrome"
|
||||||
node --max-old-space-size=2048 scripts/build-zip.js chrome nightly
|
node scripts/build-zip.js chrome nightly
|
||||||
|
|
||||||
#
|
#
|
||||||
#./scripts/build-crx.sh
|
#./scripts/build-crx.sh
|
||||||
#
|
#
|
||||||
|
|
||||||
#
|
|
||||||
# build edge
|
|
||||||
#
|
|
||||||
npm run "${BUILD_SCRIPT}-edge"
|
|
||||||
node --max-old-space-size=2048 scripts/build-zip.js chrome nightly
|
|
||||||
|
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
# UPLOAD TO WEB SERVER
|
# UPLOAD TO WEB SERVER
|
||||||
######################################
|
######################################
|
||||||
@ -85,6 +76,7 @@ echo "Uploading to server ..."
|
|||||||
scp -i ~/.ssh/id_rsa -r ./dist-zip/* "ultrawidify-uploader@${RELEASE_SERVER}:${RELEASE_DIRECTORY}${BUILD_CHANNEL_DIRECTORY}"
|
scp -i ~/.ssh/id_rsa -r ./dist-zip/* "ultrawidify-uploader@${RELEASE_SERVER}:${RELEASE_DIRECTORY}${BUILD_CHANNEL_DIRECTORY}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
# Build finished message
|
# Build finished message
|
||||||
######################################
|
######################################
|
||||||
|
@ -2,46 +2,4 @@
|
|||||||
|
|
||||||
# makes a zip file with human-readable source code that we need to upload to AMO
|
# makes a zip file with human-readable source code that we need to upload to AMO
|
||||||
# since webpack minifies stuff
|
# since webpack minifies stuff
|
||||||
|
zip -r dist-zip/uw-amo-source.zip README-AMO.md .babelrc package.json package-lock.json webpack.config.js src/
|
||||||
# first, we collect npm/node versions:
|
|
||||||
# (NOTE: the last bit is necessary to remove ANSI color codes from output)
|
|
||||||
NODE_VERSION=`node --version`
|
|
||||||
NPM_VERSION=`npm --version`
|
|
||||||
LINUX_VERSION="$(uname -a | sed 's/\//\\\//g')"
|
|
||||||
|
|
||||||
# copy REAMDE to /tmp for processing
|
|
||||||
cp README-AMO.md /tmp/README-AMO.md
|
|
||||||
|
|
||||||
# replace placeholders with proper software versions
|
|
||||||
sed -i "s/%%NODE_VERSION%%/${NODE_VERSION}/g" /tmp/README-AMO.md
|
|
||||||
sed -i "s/%%NPM_VERSION%%/${NPM_VERSION}/g" /tmp/README-AMO.md
|
|
||||||
sed -i "s/%%LINUX_VERSION%%/${LINUX_VERSION}/g" /tmp/README-AMO.md
|
|
||||||
|
|
||||||
# add files to archive
|
|
||||||
zip -r dist-zip/uw-amo-source.zip /tmp/README-AMO.md .babelrc package.json package-lock.json webpack.config.js scripts/ src/
|
|
||||||
|
|
||||||
# rename /tmp/README-AMO.md to README.md
|
|
||||||
printf "@ tmp/README-AMO.md\n@=README.md\n" | zipnote -w dist-zip/uw-amo-source.zip
|
|
||||||
|
|
||||||
# printout for debugging purposes:
|
|
||||||
echo ""
|
|
||||||
echo "—————— AMO SOURCE PREPARATION FINISHED ——————"
|
|
||||||
echo " Debug info:"
|
|
||||||
echo ""
|
|
||||||
echo "node --version:"
|
|
||||||
node --version
|
|
||||||
echo ""
|
|
||||||
echo "npm --version"
|
|
||||||
npm --version
|
|
||||||
echo ""
|
|
||||||
echo "uname -a"
|
|
||||||
uname -a
|
|
||||||
echo ""
|
|
||||||
echo ""
|
|
||||||
echo "extracted variables:"
|
|
||||||
echo "NODE_VERSION: ${NODE_VERSION}"
|
|
||||||
echo "NPM_VERSION: ${NPM_VERSION}"
|
|
||||||
echo "UNAME: ${LINUX_VERSION}"
|
|
||||||
echo ""
|
|
||||||
echo ""
|
|
||||||
echo "—————— EXTENSION PACKAGES READY FOR UPLOAD ——————"
|
|
||||||
|
@ -5,7 +5,8 @@ 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',
|
'popup/popup.js',
|
||||||
|
'options/options.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,57 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
### VARIABLES
|
|
||||||
|
|
||||||
REPRODUCABILITY=5 # how many times we need to build with same hash
|
|
||||||
|
|
||||||
### TEST STARTS HERE ###
|
|
||||||
|
|
||||||
CURRENT_DIR=$(pwd)
|
|
||||||
|
|
||||||
if [[ $(echo $0 | awk -F"/" '{print NF-1}') -eq 1 ]] ; then
|
|
||||||
# we need to go one directory up
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
# cleanup after self if we already ran and previous test crashed or
|
|
||||||
# something before it cleaned up after itself.
|
|
||||||
rm -rf /tmp/uw-test-runs/* 2>/dev/null # we not interested if fails
|
|
||||||
|
|
||||||
# make new folder if it doesn't exist
|
|
||||||
mkdir -p /tmp/uw-test-runs
|
|
||||||
|
|
||||||
for ((run=0;run<$REPRODUCABILITY;run++)) ; do
|
|
||||||
echo ""
|
|
||||||
echo "---- run ${run} ----"
|
|
||||||
|
|
||||||
# save hash and then run build. Save hash.
|
|
||||||
OLD_HASH=$HASH
|
|
||||||
HASH=$(npm run build 2>/dev/null | grep "Hash:")
|
|
||||||
|
|
||||||
# move build file to /tmp, where it'll be saved for later
|
|
||||||
mv dist-ff /tmp/uw-test-runs/${run}
|
|
||||||
|
|
||||||
# skip comparisons with previous tests on first run,
|
|
||||||
# cos we don't have anything to compare against yet
|
|
||||||
if [[ $run -ne 0 ]] ; then
|
|
||||||
if [[ "$OLD_HASH" == "$HASH" ]] ; then
|
|
||||||
echo "Hashes ok${HASH##*:}"
|
|
||||||
else
|
|
||||||
echo "Webpack hashes do not match! (${OLD_HASH##*:} <---> ${HASH##*:})"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Diff test (no output=ok):"
|
|
||||||
diff -qr /tmp/uw-test-runs/${prev_run} /tmp/uw-test-runs/${run}
|
|
||||||
else
|
|
||||||
echo "Webpack hash: $HASH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# save id of previous run
|
|
||||||
prev_run=$run
|
|
||||||
done
|
|
||||||
|
|
||||||
# clean up after self
|
|
||||||
rm -rf /tmp/uw-test-runs
|
|
||||||
|
|
||||||
# restore dir
|
|
||||||
cd "$CURRENT_DIR"
|
|
@ -5,14 +5,14 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex action-name">
|
<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-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.userAdded || (action.cmd[0].arg === AspectRatio.Fixed)" class="icon red" @click="removeAction()">🗙</span>
|
||||||
<span v-else class="icon transparent">🗙</span>
|
<span v-else class="icon transparent">🗙</span>
|
||||||
<span class="icon" @click="editAction()">🖉</span>
|
<span class="icon" @click="editAction()">🖉</span>
|
||||||
{{action.name}}
|
{{action.name}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="command-details">
|
<div class="command-details">
|
||||||
<div class="flex flex-col cmd-container cd-pad">
|
<div class="flex flex-column cmd-container cd-pad">
|
||||||
<div class="flex bold">
|
<div class="flex bold">
|
||||||
Command:
|
Command:
|
||||||
</div>
|
</div>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SCOPES -->
|
<!-- SCOPES -->
|
||||||
<div class="flex flex-col scopes-container cd-pad">
|
<div class="flex flex-column scopes-container cd-pad">
|
||||||
<div class="flex bold">Popup tabs:</div>
|
<div class="flex bold">Popup tabs:</div>
|
||||||
|
|
||||||
<!-- global scope -->
|
<!-- global scope -->
|
||||||
@ -108,16 +108,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import StretchType from '../enums/StretchType.enum';
|
import Stretch from '../enums/stretch.enum';
|
||||||
import AspectRatioType from '../enums/AspectRatioType.enum';
|
import AspectRatio from '../enums/aspect-ratio.enum';
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
|
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
StretchType: StretchType,
|
Stretch: Stretch,
|
||||||
AspectRatioType: AspectRatioType,
|
AspectRatio: AspectRatio,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@ -244,4 +244,5 @@ export default {
|
|||||||
.transparent {
|
.transparent {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -46,13 +46,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import StretchType from '../enums/StretchType.enum';
|
import Stretch from '../enums/stretch.enum';
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
|
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
StretchType: StretchType
|
Stretch: Stretch
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
17
src/common/components/Button.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div class="button center-text flex"
|
||||||
|
:class="{'setting-selected': selected, 'w24': fixedWidth, 'flex-auto': !fixedWidth }"
|
||||||
|
>
|
||||||
|
{{label}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
fixedWidth: Boolean,
|
||||||
|
selected: Boolean,
|
||||||
|
label: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
83
src/common/components/JsonEditor/JsonArray.vue
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-column 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>
|
90
src/common/components/JsonEditor/JsonElement.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<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>
|
91
src/common/components/JsonEditor/JsonObject.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-column 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>
|
23
src/common/components/JsonEditor/json.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.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);
|
||||||
|
}
|
@ -21,7 +21,7 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style>
|
||||||
.mt2px {
|
.mt2px {
|
||||||
margin-top: 7px;
|
margin-top: 7px;
|
||||||
}
|
}
|
@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-column">
|
||||||
<div v-if="!editing && !adding" class="flex flex-row">
|
<div v-if="!editing && !adding" class="flex flex-row">
|
||||||
<div class="">
|
<div class="">
|
||||||
<b>Query selector:</b> {{qs.string}}<br/>
|
<b>Query selector:</b> {{qs.string}}<br/>
|
||||||
<b>Additional CSS:</b> {{qs.css || 'no style rules'}}
|
<b>Additional CSS:</b> {{qs.css || 'no style rules'}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col flex-nogrow">
|
<div class="flex flex-column flex-nogrow">
|
||||||
<a @click="editing = true">Edit</a>
|
<a @click="editing = true">Edit</a>
|
||||||
<a @click="$emit('delete')">Delete</a>
|
<a @click="$emit('delete')">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex flex-row">
|
<div v-else class="flex flex-row">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-column">
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<div class="flex label-secondary form-label">
|
<div class="flex label-secondary form-label">
|
||||||
Query selector:
|
Query selector:
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col flex-center">
|
<div class="flex flex-column flex-center">
|
||||||
<div class="flex flex-self-center">
|
<div class="flex flex-self-center">
|
||||||
{{label}}
|
{{label}}
|
||||||
</div>
|
</div>
|
||||||
@ -20,7 +20,7 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style scoped>
|
||||||
.center-text {
|
.center-text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
@ -1,14 +0,0 @@
|
|||||||
let Notifications = Object.freeze({
|
|
||||||
'TEST_NOTIFICATION': {
|
|
||||||
icon: 'card-text',
|
|
||||||
text: 'This is a test notification.',
|
|
||||||
timeout: -1,
|
|
||||||
},
|
|
||||||
'AARD_DRM': {
|
|
||||||
icon: 'exclamation-triangle',
|
|
||||||
text: '<b>Autodetection may not be able to run on this video.</b> On sites that utilize DRM you will have to set aspect ratio manually.',
|
|
||||||
timeout: 5000,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Notifications;
|
|
@ -1,7 +0,0 @@
|
|||||||
enum AntiGradientMode {
|
|
||||||
Disabled = 0,
|
|
||||||
Lax = 1,
|
|
||||||
Strict = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AntiGradientMode;
|
|
@ -1,13 +0,0 @@
|
|||||||
enum AspectRatioType {
|
|
||||||
Cycle = -2,
|
|
||||||
Initial = -1, // page default
|
|
||||||
Reset = 0, // reset to initial
|
|
||||||
Automatic = 1, // we want to request automatic aspect ratio detection
|
|
||||||
FitWidth = 2, // legacy/dynamic = fit to width
|
|
||||||
FitHeight = 3, // legacy/dynamic = fit to height
|
|
||||||
Fixed = 4, // pre-determined aspect ratio
|
|
||||||
Manual = 5, // ratio achieved by zooming in/zooming out
|
|
||||||
AutomaticUpdate = 6, // set by Aard
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AspectRatioType;
|
|
@ -1,9 +0,0 @@
|
|||||||
enum CropModePersistence {
|
|
||||||
Default = -1,
|
|
||||||
Disabled = 0,
|
|
||||||
UntilPageReload = 1,
|
|
||||||
CurrentSession = 2,
|
|
||||||
Forever = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CropModePersistence;
|
|
@ -1,10 +0,0 @@
|
|||||||
enum ExtensionMode {
|
|
||||||
AutoDisabled = -2,
|
|
||||||
Disabled = -1,
|
|
||||||
Default = 0,
|
|
||||||
Whitelist = 1,
|
|
||||||
Basic = 2,
|
|
||||||
Enabled = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ExtensionMode;
|
|
@ -1,11 +0,0 @@
|
|||||||
enum StretchType {
|
|
||||||
NoStretch = 0,
|
|
||||||
Basic = 1,
|
|
||||||
Hybrid = 2,
|
|
||||||
Conditional = 3,
|
|
||||||
Fixed = 4,
|
|
||||||
FixedSource = 5,
|
|
||||||
Default = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StretchType;
|
|
@ -1,10 +0,0 @@
|
|||||||
enum VideoAlignmentType {
|
|
||||||
Left = 0,
|
|
||||||
Center = 1,
|
|
||||||
Right = 2,
|
|
||||||
Top = 3,
|
|
||||||
Bottom = 4,
|
|
||||||
Default = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VideoAlignmentType;
|
|
7
src/common/enums/anti-gradient-mode.enum.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
var AntiGradientMode = Object.freeze({
|
||||||
|
Disabled: 0,
|
||||||
|
Lax: 1,
|
||||||
|
Strict: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AntiGradientMode;
|
10
src/common/enums/aspect-ratio.enum.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
var AspectRatio = Object.freeze({
|
||||||
|
Initial: -1,
|
||||||
|
Reset: 0,
|
||||||
|
Automatic: 1,
|
||||||
|
FitWidth: 2,
|
||||||
|
FitHeight: 3,
|
||||||
|
Fixed: 4,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AspectRatio;
|
9
src/common/enums/crop-mode-persistence.enum.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
var CropModePersistence = Object.freeze({
|
||||||
|
Default: -1,
|
||||||
|
Disabled: 0,
|
||||||
|
UntilPageReload: 1,
|
||||||
|
CurrentSession: 2,
|
||||||
|
Forever: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CropModePersistence;
|
15
src/common/enums/extension-mode.enum.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
if (process.env.CHANNEL !== 'stable') {
|
||||||
|
console.log('Loaded ExtensionMode');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ExtensionMode = Object.freeze({
|
||||||
|
AutoDisabled: -2,
|
||||||
|
Disabled: -1,
|
||||||
|
Default: 0,
|
||||||
|
Whitelist: 1,
|
||||||
|
Basic: 2,
|
||||||
|
Enabled: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ExtensionMode;
|
11
src/common/enums/stretch.enum.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
var Stretch = Object.freeze({
|
||||||
|
NoStretch: 0,
|
||||||
|
Basic: 1,
|
||||||
|
Hybrid: 2,
|
||||||
|
Conditional: 3,
|
||||||
|
Fixed: 4,
|
||||||
|
FixedSource: 5,
|
||||||
|
Default: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Stretch;
|
8
src/common/enums/video-alignment.enum.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var VideoAlignment = Object.freeze({
|
||||||
|
Left: 0,
|
||||||
|
Center: 1,
|
||||||
|
Right: 2,
|
||||||
|
Default: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoAlignment;
|
@ -1,13 +0,0 @@
|
|||||||
import AspectRatioType from '../enums/AspectRatioType.enum';
|
|
||||||
|
|
||||||
export enum ArVariant {
|
|
||||||
Crop = undefined,
|
|
||||||
Zoom = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Ar {
|
|
||||||
type: AspectRatioType,
|
|
||||||
ratio?: number,
|
|
||||||
variant?: ArVariant,
|
|
||||||
offset?: number,
|
|
||||||
}
|
|
@ -1,371 +0,0 @@
|
|||||||
import { Action } from '../../../node_modules/vuex/types/index'
|
|
||||||
import AntiGradientMode from '../enums/AntiGradientMode.enum'
|
|
||||||
import AspectRatioType from '../enums/AspectRatioType.enum'
|
|
||||||
import CropModePersistence from '../enums/CropModePersistence.enum'
|
|
||||||
import ExtensionMode from '../enums/ExtensionMode.enum'
|
|
||||||
import StretchType from '../enums/StretchType.enum'
|
|
||||||
import VideoAlignmentType from '../enums/VideoAlignmentType.enum'
|
|
||||||
|
|
||||||
export enum ExtensionEnvironment {
|
|
||||||
Normal = 'normal',
|
|
||||||
Theater = 'theater',
|
|
||||||
Fullscreen = 'fullscreen',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DevUiConfig {
|
|
||||||
aardDebugOverlay: {
|
|
||||||
showOnStartup: boolean,
|
|
||||||
showDetectionDetails: boolean,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface KeyboardShortcutInterface {
|
|
||||||
key?: string,
|
|
||||||
code?: string,
|
|
||||||
ctrlKey?: boolean,
|
|
||||||
metaKey?: boolean,
|
|
||||||
altKey?: boolean,
|
|
||||||
shiftKey?: boolean,
|
|
||||||
onKeyUp?: boolean,
|
|
||||||
onKeyDown?: boolean,
|
|
||||||
onMouseMove?: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ActionScopeInterface {
|
|
||||||
show: boolean,
|
|
||||||
label?: string, // example override, takes precedence over default label
|
|
||||||
shortcut?: KeyboardShortcutInterface[],
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RestrictionsSettings {
|
|
||||||
disableOnSmallPlayers?: boolean; // Whether ultrawidify should disable itself when the player is small
|
|
||||||
minAllowedWidth?: number; // if player is less than this many px wide, ultrawidify will disable itself
|
|
||||||
minAllowedHeight?: number; // if player is less than this many px tall, ultrawidify will disable itself
|
|
||||||
onlyAllowInFullscreen?: boolean; // if enabled, ultrawidify will be disabled when not in full screen regardless of what previous two options say
|
|
||||||
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExtensionEnvironmentSettingsInterface {
|
|
||||||
normal: ExtensionMode,
|
|
||||||
theater: ExtensionMode,
|
|
||||||
fullscreen: ExtensionMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommandInterface {
|
|
||||||
action: string,
|
|
||||||
label: string,
|
|
||||||
comment?: string,
|
|
||||||
arguments?: any,
|
|
||||||
shortcut?: KeyboardShortcutInterface,
|
|
||||||
internalOnly?: boolean,
|
|
||||||
actionId?: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SettingsReloadComponent = 'PlayerData' | 'VideoData';
|
|
||||||
export type SettingsReloadFlags = true | SettingsReloadComponent;
|
|
||||||
|
|
||||||
export interface AardSettings {
|
|
||||||
aardType: 'webgl' | 'legacy' | 'auto';
|
|
||||||
|
|
||||||
earlyStopOptions: {
|
|
||||||
stopAfterFirstDetection: boolean;
|
|
||||||
stopAfterTimeout: boolean;
|
|
||||||
stopTimeout: number;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
},
|
|
||||||
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
|
|
||||||
consecutiveArResets: number // if aspect ratio reverts immediately after AR change is applied, we disable everything
|
|
||||||
},
|
|
||||||
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
|
|
||||||
gradientTestMinDelta: number, // if last pixels of the test sample is less than this brighter than the first -> 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SettingsInterface {
|
|
||||||
_updateFlags?: {
|
|
||||||
requireReload?: SettingsReloadFlags,
|
|
||||||
forSite?: string
|
|
||||||
}
|
|
||||||
dev: DevSettings,
|
|
||||||
|
|
||||||
arDetect: AardSettings,
|
|
||||||
|
|
||||||
ui: {
|
|
||||||
inPlayer: {
|
|
||||||
enabled: boolean, // Deprecated — moved to site settings
|
|
||||||
enabledFullscreenOnly: boolean, // Deprecated — moved to site settings
|
|
||||||
popupAlignment: 'left' | 'right',
|
|
||||||
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: 'trigger-zone' | 'player', // what needs to be hovered in order for UI to be visible
|
|
||||||
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]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
devMode?: boolean,
|
|
||||||
dev: DevUiConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
restrictions?: RestrictionsSettings;
|
|
||||||
|
|
||||||
crop: {
|
|
||||||
default: any;
|
|
||||||
},
|
|
||||||
stretch: {
|
|
||||||
default: any;
|
|
||||||
conditionalDifferencePercent: number // black bars less than this wide will trigger stretch
|
|
||||||
// if mode is set to '1'. 1.0=100%
|
|
||||||
},
|
|
||||||
kbm: {
|
|
||||||
enabled: boolean, // if keyboard/mouse handler service will run
|
|
||||||
keyboardEnabled: boolean, // if keyboard shortcuts are processed
|
|
||||||
mouseEnabled: boolean, // if mouse movement is processed
|
|
||||||
}
|
|
||||||
|
|
||||||
zoom: {
|
|
||||||
minLogZoom: number,
|
|
||||||
maxLogZoom: number,
|
|
||||||
announceDebounce: number // we wait this long before announcing new zoom
|
|
||||||
},
|
|
||||||
|
|
||||||
miscSettings: {
|
|
||||||
mousePan: {
|
|
||||||
enabled: boolean
|
|
||||||
},
|
|
||||||
mousePanReverseMouse: boolean,
|
|
||||||
defaultAr?: any
|
|
||||||
},
|
|
||||||
|
|
||||||
resizer: {
|
|
||||||
setStyleString: {
|
|
||||||
maxRetries: number,
|
|
||||||
retryTimeout: number
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pageInfo: {
|
|
||||||
timeouts: {
|
|
||||||
urlCheck: number,
|
|
||||||
rescan: number
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pan?: any,
|
|
||||||
version?: string,
|
|
||||||
preventReload?: boolean,
|
|
||||||
lastModified?: Date,
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// ::: MITIGATIONS :::
|
|
||||||
// -----------------------------------------
|
|
||||||
// Settings for browser bug workarounds.
|
|
||||||
mitigations?: {
|
|
||||||
zoomLimit?: {
|
|
||||||
enabled?: boolean,
|
|
||||||
fullscreenOnly?: boolean,
|
|
||||||
limit?: number,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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.
|
|
||||||
commands?: {
|
|
||||||
crop?: CommandInterface[],
|
|
||||||
stretch?: CommandInterface[],
|
|
||||||
zoom?: CommandInterface[],
|
|
||||||
pan?: CommandInterface[],
|
|
||||||
internal?: CommandInterface[],
|
|
||||||
},
|
|
||||||
whatsNewChecked: boolean,
|
|
||||||
newFeatureTracker: any,
|
|
||||||
// -----------------------------------------
|
|
||||||
// ::: SITE CONFIGURATION :::
|
|
||||||
// -----------------------------------------
|
|
||||||
// Config for a given page:
|
|
||||||
//
|
|
||||||
// <hostname> : {
|
|
||||||
// status: <option> // should extension work on this site?
|
|
||||||
// arStatus: <option> // should we do autodetection on this site?
|
|
||||||
//
|
|
||||||
// defaultAr?: <ratio> // automatically apply this aspect ratio on this side. Use extension defaults if undefined.
|
|
||||||
// stretch? <stretch mode> // automatically stretch video on this site in this manner
|
|
||||||
// videoAlignment? <left|center|right>
|
|
||||||
//
|
|
||||||
// type: <official|community|user> // 'official' — blessed by Tam.
|
|
||||||
// // 'community' — blessed by reddit.
|
|
||||||
// // 'user' — user-defined (not here)
|
|
||||||
// override: <true|false> // override user settings for this site on update
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Valid values for options:
|
|
||||||
//
|
|
||||||
// status, arStatus, statusEmbedded:
|
|
||||||
//
|
|
||||||
// * enabled — always allow, full
|
|
||||||
// * basic — allow, but only the basic version without playerData
|
|
||||||
// * default — allow if default is to allow, block if default is to block
|
|
||||||
// * disabled — never allow
|
|
||||||
//
|
|
||||||
sites: {
|
|
||||||
[x: string]: SiteSettingsInterface,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SiteSettingsInterface {
|
|
||||||
enable: ExtensionEnvironmentSettingsInterface;
|
|
||||||
enableAard: ExtensionEnvironmentSettingsInterface;
|
|
||||||
enableKeyboard: ExtensionEnvironmentSettingsInterface;
|
|
||||||
enableUI: ExtensionEnvironmentSettingsInterface; // Lies! enableUI doesn't use 'theater' property (but uses the other two)
|
|
||||||
|
|
||||||
autocreated?: boolean;
|
|
||||||
type?: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
|
|
||||||
defaultType: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
|
|
||||||
|
|
||||||
// must be defined in @global and @empty
|
|
||||||
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment
|
|
||||||
|
|
||||||
defaults?: { // must be defined in @global and @empty
|
|
||||||
crop?: {type: AspectRatioType, [x: string]: any},
|
|
||||||
stretch?: {type: StretchType, ratio?: number},
|
|
||||||
alignment?: {x: VideoAlignmentType, y: VideoAlignmentType},
|
|
||||||
}
|
|
||||||
|
|
||||||
cropModePersistence?: CropModePersistence;
|
|
||||||
stretchModePersistence?: CropModePersistence;
|
|
||||||
alignmentPersistence?: CropModePersistence;
|
|
||||||
|
|
||||||
playerAutoConfig?: PlayerAutoConfigInterface;
|
|
||||||
|
|
||||||
activeDOMConfig?: string;
|
|
||||||
DOMConfig?: { [x: string]: SiteDOMSettingsInterface };
|
|
||||||
|
|
||||||
// the following script are for extension caching and shouldn't be saved.
|
|
||||||
// if they _are_ saved, they will be overwritten
|
|
||||||
currentDOMConfig?: SiteDOMSettingsInterface;
|
|
||||||
|
|
||||||
// the following fields are for use with extension update 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 {
|
|
||||||
type: 'official' | 'community' | 'user-defined' | 'modified' | undefined;
|
|
||||||
elements?: {
|
|
||||||
player?: SiteDOMElementSettingsInterface,
|
|
||||||
video?: SiteDOMElementSettingsInterface,
|
|
||||||
other?: { [x: number]: SiteDOMElementSettingsInterface }
|
|
||||||
};
|
|
||||||
customCss?: string;
|
|
||||||
periodicallyRefreshPlayerElement?: boolean;
|
|
||||||
|
|
||||||
// the following script are for extension caching and shouldn't be saved.
|
|
||||||
// if they _are_ saved, they will be overwritten
|
|
||||||
anchorElementIndex?: number;
|
|
||||||
anchorElement?: HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SiteDOMElementSettingsInterface {
|
|
||||||
manual?: boolean;
|
|
||||||
querySelectors?: string;
|
|
||||||
index?: number; // previously: useRelativeAncestor + videoAncestor
|
|
||||||
mode?: 'index' | 'qs';
|
|
||||||
nodeCss?: {[x: string]: string};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SettingsInterface;
|
|
@ -1,7 +0,0 @@
|
|||||||
import StretchType from '../enums/StretchType.enum';
|
|
||||||
|
|
||||||
export interface Stretch {
|
|
||||||
type: StretchType,
|
|
||||||
ratio?: number,
|
|
||||||
limit?: number,
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* For some reason, Chrome really doesn't like when chrome.runtime
|
|
||||||
* methods are wrapped inside a ES6 proxy object. If 'port' is a
|
|
||||||
* ES6 Proxy of a Port object that `chrome.runtime.connect()` creates,
|
|
||||||
* then Chrome will do bullshits like `port.sendMessage` and
|
|
||||||
* `port.onMessage.addListener` crashing your Vue3 UI with bullshits
|
|
||||||
* excuses, e.g.
|
|
||||||
*
|
|
||||||
* | TypeError: Illegal invocation. Function must be called on
|
|
||||||
* | an object of type Port
|
|
||||||
*
|
|
||||||
* which is some grade A bullshit because Firefox can handle that just
|
|
||||||
* fine.
|
|
||||||
*
|
|
||||||
* There's two ways how I could handle this:
|
|
||||||
* * Find out how to get the original object from the proxy Vue3
|
|
||||||
* creates, which would take time and ruin my xmass holiday, or
|
|
||||||
* * make a global object with a passive-aggressive name and ignore
|
|
||||||
* the very real possibility that there's prolly a reason Chrome
|
|
||||||
* does things in its own very special(tm) way, as if it had one
|
|
||||||
* extra chromosome over Firefox.
|
|
||||||
*
|
|
||||||
* Easy choice, really.
|
|
||||||
*/
|
|
||||||
export class ChromeShittinessMitigations {
|
|
||||||
static port = null;
|
|
||||||
|
|
||||||
static setProperty(property, value) {
|
|
||||||
ChromeShittinessMitigations[property] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChromeShittinessMitigations;
|
|
@ -21,20 +21,6 @@ class KeyboardShortcutParser {
|
|||||||
}
|
}
|
||||||
return shortcutCombo;
|
return shortcutCombo;
|
||||||
}
|
}
|
||||||
|
|
||||||
static generateShortcutFromKeypress(event) {
|
|
||||||
return {
|
|
||||||
ctrlKey: event.ctrlKey,
|
|
||||||
altKey: event.altKey,
|
|
||||||
shiftKey: event.shiftKey,
|
|
||||||
metaKey: event.metaKey,
|
|
||||||
code: event.code,
|
|
||||||
key: event.key,
|
|
||||||
keyup: true,
|
|
||||||
keydown: false,
|
|
||||||
type: event.type, // only needed for purposes of EditShortcutButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default KeyboardShortcutParser;
|
export default KeyboardShortcutParser;
|
@ -1,3 +1,3 @@
|
|||||||
export async function sleep(timeout) {
|
export async function sleep(timeout) {
|
||||||
return new Promise( (resolve, reject) => setTimeout(() => resolve(null), timeout));
|
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
|
||||||
}
|
}
|
@ -1,18 +0,0 @@
|
|||||||
export async function sleep(timeout) {
|
|
||||||
return new Promise<void>( (resolve, reject) => setTimeout(() => resolve(), timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates deep copy of an object
|
|
||||||
* @param obj
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function _cp(obj) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(JSON.stringify(obj));
|
|
||||||
} catch (e) {
|
|
||||||
// console.error('Failed to parse json. This probably means that the data we received was not an object. Will return data as-is');
|
|
||||||
// console.error('data in:', obj, 'error:', e);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
scopeActions: function() {
|
scopeActions: function() {
|
||||||
return this.settings?.active.actions?.filter(x => {
|
return this.settings.active.actions.filter(x => {
|
||||||
if (! x.scopes) {
|
if (! x.scopes) {
|
||||||
console.error('This action does not have a scope.', x);
|
console.error('This action does not have a scope.', x);
|
||||||
return false;
|
return false;
|
||||||
@ -10,7 +10,7 @@ export default {
|
|||||||
}) || [];
|
}) || [];
|
||||||
},
|
},
|
||||||
extensionActions: function(){
|
extensionActions: function(){
|
||||||
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-ExtensionMode') || [];
|
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-extension-mode') || [];
|
||||||
},
|
},
|
||||||
aardActions: function(){
|
aardActions: function(){
|
||||||
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-autoar-mode') || [];
|
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-autoar-mode') || [];
|
@ -1,62 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="uw-clickthrough relative w-full h-full">
|
|
||||||
<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() {
|
|
||||||
/**
|
|
||||||
* 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>
|
|
437
src/csui/LoggerUi.vue
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="showLoggerUi" class="root-window flex flex-column overflow-hidden"
|
||||||
|
@keyup.stop
|
||||||
|
@keydown.stop
|
||||||
|
@keypress.stop
|
||||||
|
>
|
||||||
|
<div class="header">
|
||||||
|
<div class="header-top flex flex-row">
|
||||||
|
<div class="flex-grow">
|
||||||
|
<h1>{{header.header}}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="button flex-noshrink button-header"
|
||||||
|
@click="hidePopup()"
|
||||||
|
>
|
||||||
|
<template v-if="logStringified">Finish logging</template>
|
||||||
|
<template v-else>Hide popup</template>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="button flex-noshrink button-header"
|
||||||
|
@click="stopLogging()"
|
||||||
|
>
|
||||||
|
Stop logging
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<div class="header-bottom">
|
||||||
|
<div>{{header.subheader}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content flex flex-row flex-grow overflow-hidden">
|
||||||
|
|
||||||
|
<!-- LOGGER SETTINGS PANEL -->
|
||||||
|
<div class="settings-panel flex flex-noshrink flex-column">
|
||||||
|
<div class="panel-top flex-nogrow">
|
||||||
|
<h2>Logger configuration</h2>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row flex-end w100">
|
||||||
|
<div v-if="!showTextMode" class="button" @click="showTextMode = true">
|
||||||
|
Paste config ...
|
||||||
|
</div>
|
||||||
|
<div v-else class="button" @click="showTextMode = false">
|
||||||
|
Back
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-middle scrollable flex-grow log-config-margin">
|
||||||
|
<template v-if="showTextMode">
|
||||||
|
<div ref="settingsEditArea"
|
||||||
|
style="white-space: pre-wrap; border: 1px solid orange; padding: 10px;"
|
||||||
|
class="monospace h100"
|
||||||
|
:class="{'jsonbg': !confHasError, 'jsonbg-error': confHasError}"
|
||||||
|
contenteditable="true"
|
||||||
|
@input="updateSettings"
|
||||||
|
>
|
||||||
|
{{parsedSettings}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<JsonObject label="logger-settings"
|
||||||
|
:value="currentSettings"
|
||||||
|
:ignoreKeys="{'allowLogging': true}"
|
||||||
|
@change="updateSettingsUi"
|
||||||
|
></JsonObject>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-end">
|
||||||
|
<div class="button" @click="restoreLoggerSettings()">
|
||||||
|
Revert
|
||||||
|
</div>
|
||||||
|
<div class="button button-primary" @click="saveLoggerSettings()">
|
||||||
|
Save
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LOGGER OUTPUT/START LOGGING -->
|
||||||
|
<div class="results-panel flex flex-shrink flex-column overflow-hidden">
|
||||||
|
<div class="panel-top flex-nogrow">
|
||||||
|
<h2>Logger results</h2>
|
||||||
|
</div>
|
||||||
|
<template v-if="logStringified">
|
||||||
|
<div v-if="confHasError" class="warn">
|
||||||
|
Logger configuration contains an error. You can export current log, but you will be unable to record a new log.
|
||||||
|
</div>
|
||||||
|
<div class="panel-middle scrollable flex-grow p-t-025em">
|
||||||
|
<pre>
|
||||||
|
{{logStringified}}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div class="flex-noshrink flex flex-row flex-end p-t-025em">
|
||||||
|
<div class="button button-bar"
|
||||||
|
@click="startLogging()"
|
||||||
|
>
|
||||||
|
New log
|
||||||
|
</div>
|
||||||
|
<div class="button button-bar"
|
||||||
|
@click="exportLog()"
|
||||||
|
>
|
||||||
|
Export log
|
||||||
|
</div>
|
||||||
|
<div class="button button-bar button-primary"
|
||||||
|
@click="exportAndQuit()"
|
||||||
|
>
|
||||||
|
Export & finish
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="panel-middle scrollable flex-grow">
|
||||||
|
<div v-if="!parsedSettings" class="text-center w100">
|
||||||
|
Please paste logger config into the text box to the left.
|
||||||
|
←←←
|
||||||
|
</div>
|
||||||
|
<div v-else-if="confHasError" class="warn">
|
||||||
|
Logger configuration contains an error. Cannot start logging.
|
||||||
|
</div>
|
||||||
|
<div v-else-if="lastSettings && lastSettings.allowLogging && lastSettings.consoleOptions && lastSettings.consoleOptions.enabled"
|
||||||
|
class="flex flex-column flex-center flex-cross-center w100 h100"
|
||||||
|
>
|
||||||
|
<p class="m-025em">
|
||||||
|
Logging in progress ...
|
||||||
|
</p>
|
||||||
|
<div class="button button-big button-primary"
|
||||||
|
@click="stopLogging()"
|
||||||
|
>
|
||||||
|
Stop logging
|
||||||
|
</div>
|
||||||
|
<template v-if="lastSettings && lastSettings.timeout"
|
||||||
|
class="m-025em"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
... or wait until logging ends.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You can <a @click="hidePopup()">hide popup</a> — it will automatically re-appear when logging finishes.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="lastSettings">
|
||||||
|
<p>
|
||||||
|
You can <a @click="hidePopup()">hide popup</a> — the logging will continue until you re-open the popup and stop it.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex flex-column flex-center flex-cross-center w100 h100">
|
||||||
|
<div class="button button-big button-primary"
|
||||||
|
@click="startLogging()"
|
||||||
|
>
|
||||||
|
Start logging
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div>
|
||||||
|
button row is heres
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import Logger from '../ext/lib/Logger';
|
||||||
|
import Comms from '../ext/lib/comms/Comms';
|
||||||
|
import IO from '../common/js/IO';
|
||||||
|
import JsonObject from '../common/components/JsonEditor/JsonObject';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
JsonObject,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showLoggerUi: false,
|
||||||
|
header: {
|
||||||
|
header: 'whoopsie daisy',
|
||||||
|
subheader: 'you broke the header choosing script'
|
||||||
|
},
|
||||||
|
parsedSettings: '',
|
||||||
|
lastSettings: {},
|
||||||
|
currentSettings: {},
|
||||||
|
confHasError: false,
|
||||||
|
logStringified: '',
|
||||||
|
showTextMode: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
const headerRotation = [{
|
||||||
|
header: "DEFORESTATOR 5000",
|
||||||
|
subheader: "Iron Legion's finest logging tool"
|
||||||
|
}, {
|
||||||
|
header: "Astinus",
|
||||||
|
subheader: "Ultrawidify logging tool"
|
||||||
|
}, {
|
||||||
|
header: "Tracer",
|
||||||
|
subheader: "I'm already printing stack traces"
|
||||||
|
}];
|
||||||
|
|
||||||
|
this.header = headerRotation[Math.floor(+Date.now() / (3600000*24)) % headerRotation.length] || this.header;
|
||||||
|
|
||||||
|
this.getLoggerSettings();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState([
|
||||||
|
'uwLog',
|
||||||
|
'showLogger',
|
||||||
|
'loggingEnded',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
uwLog(newValue, oldValue) {
|
||||||
|
if (oldValue !== newValue) {
|
||||||
|
this.$store.dispatch('uw-show-logger');
|
||||||
|
this.logStringified = JSON.stringify(newValue, null, 2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async showLogger(newValue) {
|
||||||
|
this.showLoggerUi = newValue;
|
||||||
|
|
||||||
|
// update logger settings (they could have changed while the popup was closed)
|
||||||
|
if (newValue) {
|
||||||
|
this.getLoggerSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loggingEnded(newValue) {
|
||||||
|
// note — the value of loggingEnded never actually matters. Even if this value is 'true'
|
||||||
|
// internally, vuexStore.dspatch() will still do its job and give us the signal we want
|
||||||
|
if (newValue) {
|
||||||
|
this.stopLogging();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getLoggerSettings() {
|
||||||
|
this.lastSettings = await Logger.getConfig() || {};
|
||||||
|
this.parsedSettings = JSON.stringify(this.lastSettings, null, 2) || '';
|
||||||
|
this.currentSettings = JSON.parse(JSON.stringify(this.lastSettings));
|
||||||
|
},
|
||||||
|
updateSettings(val) {
|
||||||
|
try {
|
||||||
|
this.parsedSettings = JSON.stringify(JSON.parse(val.target.textContent.trim()), null, 2);
|
||||||
|
this.lastSettings = JSON.parse(val.target.textContent.trim());
|
||||||
|
this.currentSettings = JSON.parse(JSON.stringify(this.lastSettings));
|
||||||
|
this.confHasError = false;
|
||||||
|
} catch (e) {
|
||||||
|
this.confHasError = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
hidePopup() {
|
||||||
|
// this function only works as 'close' if logging has finished
|
||||||
|
if (this.logStringified) {
|
||||||
|
Logger.saveConfig({...this.lastSettings, allowLogging: false});
|
||||||
|
this.logStringified = undefined;
|
||||||
|
}
|
||||||
|
this.$store.dispatch('uw-hide-logger');
|
||||||
|
},
|
||||||
|
closePopupAndStopLogging() {
|
||||||
|
Logger.saveConfig({...this.lastSettings, allowLogging: false});
|
||||||
|
this.logStringified = undefined;
|
||||||
|
this.$store.dispatch('uw-hide-logger');
|
||||||
|
},
|
||||||
|
stopLogging() {
|
||||||
|
Logger.saveConfig({...this.lastSettings, allowLogging: false});
|
||||||
|
this.lastSettings.allowLogging = false;
|
||||||
|
},
|
||||||
|
exportLog() {
|
||||||
|
IO.csStringToFile(this.logStringified);
|
||||||
|
},
|
||||||
|
exportAndQuit() {
|
||||||
|
this.exportLog();
|
||||||
|
this.logStringified = undefined;
|
||||||
|
this.closePopupAndStopLogging();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" src="../res/css/flex.scss" scoped></style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../res/css/colors.scss';
|
||||||
|
@import '../res/css/font/overpass.css';
|
||||||
|
@import '../res/css/font/overpass-mono.css';
|
||||||
|
@import '../res/css/common.scss';
|
||||||
|
|
||||||
|
|
||||||
|
.root-window {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 5vh !important;
|
||||||
|
left: 5vw !important;
|
||||||
|
width: 90vw !important;
|
||||||
|
height: 90vh !important;
|
||||||
|
z-index: 999999 !important;
|
||||||
|
background-color: rgba( $page-background, 0.9) !important;
|
||||||
|
color: #f1f1f1 !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
font-family: 'Overpass';
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
font-family: 'Overpass Thin';
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 4em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
h1 {
|
||||||
|
margin-bottom: -0.20em;
|
||||||
|
margin-top: 0.0em;
|
||||||
|
}
|
||||||
|
.header-top, .header-bottom {
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
.header-top {
|
||||||
|
background-color: $popup-header-background !important;
|
||||||
|
}
|
||||||
|
.header-bottom {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px 32px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.settings-panel {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-right: 8px;
|
||||||
|
flex-grow: 2 !important;
|
||||||
|
min-width: 30% !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
height: inherit !important;
|
||||||
|
}
|
||||||
|
.results-panel {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 8px;
|
||||||
|
max-width: 70% !important;
|
||||||
|
flex-grow: 5 !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
height: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollable {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overflow-hidden {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: 'Overpass Mono';
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-025em {
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-t-025em {
|
||||||
|
padding-top: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
padding-left: 2em;
|
||||||
|
padding-right: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary {
|
||||||
|
background-color: $primary;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-big {
|
||||||
|
font-size: 1.5em;
|
||||||
|
padding: 1.75em 3.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-bar {
|
||||||
|
font-size: 1.25em;
|
||||||
|
padding: 0.25em 1.25em;
|
||||||
|
margin-left: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-header {
|
||||||
|
font-size: 2em;
|
||||||
|
padding-top: 0.1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsonbg {
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
.jsonbg-error {
|
||||||
|
background-color: #884420;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-config-margin {
|
||||||
|
margin-top: 3em;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -1,757 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="context-spawn uw-ui-area"
|
|
||||||
style="z-index: 1000;"
|
|
||||||
v-if="!triggerZoneEditorVisible"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="spawn-container uw-ui-trigger"
|
|
||||||
:style="triggerZoneStyles"
|
|
||||||
>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="contextMenuActive || settingsInitialized && uwTriggerZoneVisible && !isGlobal"
|
|
||||||
class="context-spawn uw-ui-area"
|
|
||||||
style="z-index: 1001"
|
|
||||||
|
|
||||||
>
|
|
||||||
<GhettoContextMenu
|
|
||||||
alignment="right" class="uw-menu"
|
|
||||||
@mouseenter="() => {preventContextMenuHide(); newFeatureViewUpdate('uw6.ui-popup')}"
|
|
||||||
@mouseleave="allowContextMenuHide()"
|
|
||||||
>
|
|
||||||
<template v-slot:activator>
|
|
||||||
Ultrawidify
|
|
||||||
</template>
|
|
||||||
<slot>
|
|
||||||
<!--
|
|
||||||
Didn't manage to ensure that extension status pops up above other menu items in less than 3 minutes with z-index,
|
|
||||||
so wrapping 'status' and 'real menu items' in two different divs, ordering them in the opposite way, and then
|
|
||||||
ensuring correct ordering with flex-direction: column-reverse ended up being easier and faster.
|
|
||||||
-->
|
|
||||||
<div class="uw-clickable menu-width flex-reverse-order">
|
|
||||||
<div style="z-index: 1000">
|
|
||||||
<GhettoContextMenu alignment="right">
|
|
||||||
<template v-slot:activator>
|
|
||||||
Crop
|
|
||||||
</template>
|
|
||||||
<slot>
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="execAction(command)"
|
|
||||||
>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
</slot>
|
|
||||||
</GhettoContextMenu>
|
|
||||||
<GhettoContextMenu alignment="right">
|
|
||||||
<template v-slot:activator>
|
|
||||||
Stretch
|
|
||||||
</template>
|
|
||||||
<slot>
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="execAction(command)"
|
|
||||||
>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
</slot>
|
|
||||||
</GhettoContextMenu>
|
|
||||||
<GhettoContextMenu alignment="right">
|
|
||||||
<template v-slot:activator>
|
|
||||||
Zoom
|
|
||||||
</template>
|
|
||||||
<slot>
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
v-for="(command, index) of settings?.active.commands.zoom"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="execAction(command)"
|
|
||||||
>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
<GhettoContextMenuItem
|
|
||||||
:disableHover="true"
|
|
||||||
>
|
|
||||||
<ZoomControl
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
/>
|
|
||||||
</GhettoContextMenuItem>
|
|
||||||
</slot>
|
|
||||||
</GhettoContextMenu>
|
|
||||||
<GhettoContextMenu alignment="right">
|
|
||||||
<template v-slot:activator>
|
|
||||||
<div class="context-item">
|
|
||||||
Align
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<slot>
|
|
||||||
<GhettoContextMenuItem :disableHover="true" :css="{'reduced-padding': true}">
|
|
||||||
<AlignmentOptionsControlComponent
|
|
||||||
:eventBus="eventBus"
|
|
||||||
>
|
|
||||||
</AlignmentOptionsControlComponent>
|
|
||||||
</GhettoContextMenuItem>
|
|
||||||
</slot>
|
|
||||||
</GhettoContextMenu>
|
|
||||||
|
|
||||||
<!-- shortcut for configuring UI -->
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
v-if="settings.active.newFeatureTracker?.['uw6.ui-popup']?.show > 0"
|
|
||||||
@click="showUwWindow('playerUiSettings')"
|
|
||||||
>
|
|
||||||
<span style="color: #fa6;">Change when and if<br/>this popup appears</span>
|
|
||||||
<span style="font-size: 0.8rem">
|
|
||||||
<span style="font-size: 0.8rem; opacity: 0.5">This menu option will show {{settings.active.newFeatureTracker?.['uw6.ui-popup']?.show}} more<br/> times; or until clicked or dismissed.<br/>
|
|
||||||
Also accessible via:<br/> <span style="font-size: 0.85em">EXTENSION SETTINGS > UI AND KEYBOARD</span>.
|
|
||||||
</span>
|
|
||||||
<br/>
|
|
||||||
<a style="color: #fa6; cursor: pointer;" @click="() => acknowledgeNewFeature('uw6.ui-popup')">Dismiss this option</a>
|
|
||||||
</span>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
|
|
||||||
<!-- -->
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
@click="showUwWindow()"
|
|
||||||
label="Extension settings"
|
|
||||||
>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
@click="showUwWindow('playerDetection')"
|
|
||||||
label="Incorrect cropping?"
|
|
||||||
>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
<GhettoContextMenuOption
|
|
||||||
@click="showUwWindow('about')"
|
|
||||||
label="Not working?"
|
|
||||||
>
|
|
||||||
</GhettoContextMenuOption>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="z-index: 10000">
|
|
||||||
<GhettoContextMenuItem
|
|
||||||
class="extension-status-messages"
|
|
||||||
:disableHover="true"
|
|
||||||
>
|
|
||||||
Site compatibility:
|
|
||||||
<SupportLevelIndicator
|
|
||||||
:siteSupportLevel="siteSupportLevel"
|
|
||||||
supportLevelStyle="font-size: 0.69rem !important;"
|
|
||||||
tooltipStyle="font-size: 0.8rem;"
|
|
||||||
>
|
|
||||||
</SupportLevelIndicator>
|
|
||||||
<div v-if="statusFlags.hasDrm" class="aard-blocked">
|
|
||||||
Autodetection blocked<br/>
|
|
||||||
by <a style="color: #fff" href="https://en.wikipedia.org/wiki/Digital_rights_management" target="_blank">DRM</a>.
|
|
||||||
</div>
|
|
||||||
<div v-else-if="statusFlags.aardErrors?.cors" class="aard-blocked">
|
|
||||||
Autodetection blocked<br/>
|
|
||||||
by site/browser (CORS).
|
|
||||||
</div>
|
|
||||||
<div v-else-if="statusFlags.aardErrors?.webglError" class="aard-blocked">
|
|
||||||
Autodetection unavailable<br/>
|
|
||||||
(webgl error)
|
|
||||||
</div>
|
|
||||||
</GhettoContextMenuItem>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</slot>
|
|
||||||
</GhettoContextMenu>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="settingsInitialized && uwWindowVisible"
|
|
||||||
class="uw-window flex flex-col uw-clickable uw-ui-area"
|
|
||||||
:class="{'fade-out': uwWindowFadeOut}"
|
|
||||||
>
|
|
||||||
<PlayerUIWindow
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:logger="logger"
|
|
||||||
:in-player="!isGlobal"
|
|
||||||
:site="site"
|
|
||||||
:defaultTab="defaultWindowTab"
|
|
||||||
@close="uwWindowVisible = false"
|
|
||||||
@preventClose="(event) => uwWindowFadeOutDisabled = event"
|
|
||||||
></PlayerUIWindow>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="triggerZoneEditorVisible"
|
|
||||||
class="context-spawn uw-ui-area"
|
|
||||||
style="z-index: 1000;"
|
|
||||||
>
|
|
||||||
<TriggerZoneEditor
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:playerDimensions="playerDimensions"
|
|
||||||
>
|
|
||||||
</TriggerZoneEditor>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import PlayerUIWindow from '@csui/src/PlayerUIWindow.vue';
|
|
||||||
import GhettoContextMenu from '@csui/src/components/GhettoContextMenu.vue';
|
|
||||||
import GhettoContextMenuItem from '@csui/src/components/GhettoContextMenuItem.vue';
|
|
||||||
import GhettoContextMenuOption from '@csui/src/components/GhettoContextMenuOption.vue';
|
|
||||||
import AlignmentOptionsControlComponent from '@csui/src/PlayerUiPanels/AlignmentOptionsControlComponent.vue';
|
|
||||||
import BrowserDetect from '@src/ext/conf/BrowserDetect';
|
|
||||||
import Settings from '@src/ext/lib/Settings';
|
|
||||||
import EventBus from '@src/ext/lib/EventBus';
|
|
||||||
import UIProbeMixin from '@csui/src/utils/UIProbeMixin';
|
|
||||||
import KeyboardShortcutParserMixin from '@csui/src/utils/KeyboardShortcutParserMixin';
|
|
||||||
import CommsMixin from '@csui/src/utils/CommsMixin';
|
|
||||||
import SupportLevelIndicator from '@csui/src/components/SupportLevelIndicator.vue';
|
|
||||||
import TriggerZoneEditor from '@csui/src/components/TriggerZoneEditor.vue';
|
|
||||||
import ZoomControl from '@csui/src/popup/player-menu/ZoomControl.vue';
|
|
||||||
import { LogAggregator } from '@src/ext/lib/logging/LogAggregator';
|
|
||||||
import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
PlayerUIWindow,
|
|
||||||
GhettoContextMenu,
|
|
||||||
GhettoContextMenuItem,
|
|
||||||
GhettoContextMenuOption,
|
|
||||||
AlignmentOptionsControlComponent,
|
|
||||||
SupportLevelIndicator,
|
|
||||||
TriggerZoneEditor,
|
|
||||||
ZoomControl,
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
UIProbeMixin,
|
|
||||||
KeyboardShortcutParserMixin,
|
|
||||||
CommsMixin
|
|
||||||
],
|
|
||||||
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,
|
|
||||||
triggerZoneEditorVisible: false,
|
|
||||||
|
|
||||||
// component properties
|
|
||||||
settings: {},
|
|
||||||
BrowserDetect: BrowserDetect,
|
|
||||||
settingsInitialized: false,
|
|
||||||
eventBus: new EventBus(),
|
|
||||||
logAggregator: null,
|
|
||||||
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,
|
|
||||||
|
|
||||||
contextMenuActive: false,
|
|
||||||
triggerZoneActive: 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,
|
|
||||||
aardErrors: undefined,
|
|
||||||
},
|
|
||||||
defaultWindowTab: 'videoSettings',
|
|
||||||
|
|
||||||
saveState: {},
|
|
||||||
siteSettings: undefined,
|
|
||||||
previewZoneVisible: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
// 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';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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.logAggregator = new LogAggregator('player-overlay');
|
|
||||||
this.logger = new ComponentLogger(this.logAggregator, 'PlayerOverlay.vue');
|
|
||||||
|
|
||||||
this.settings = new Settings({afterSettingsSaved: this.updateConfig, logger: this.logger});
|
|
||||||
this.settings.listenAfterChange(() => this.updateTriggerZones());
|
|
||||||
|
|
||||||
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.subscribeMulti(
|
|
||||||
{
|
|
||||||
'uw-config-broadcast': {
|
|
||||||
function:
|
|
||||||
(data) => {
|
|
||||||
switch (data.type) {
|
|
||||||
case 'drm-status':
|
|
||||||
this.statusFlags.hasDrm = data.hasDrm;
|
|
||||||
break;
|
|
||||||
case 'aard-error':
|
|
||||||
this.statusFlags.aardErrors = data.aardErrors;
|
|
||||||
break;
|
|
||||||
case 'player-dimensions':
|
|
||||||
this.playerDimensionsUpdate(data.data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'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 = {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'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 = {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'ui-trigger-zone-update': {
|
|
||||||
function: (data) => {
|
|
||||||
this.showTriggerZonePreview = data.previewZoneVisible;
|
|
||||||
// this.;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'start-trigger-zone-edit': {
|
|
||||||
function: () => {
|
|
||||||
this.triggerZoneEditorVisible = true;
|
|
||||||
this.uwWindowVisible = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'finish-trigger-zone-edit': {
|
|
||||||
function: () => {
|
|
||||||
this.triggerZoneEditorVisible = false;
|
|
||||||
this.showUwWindow('playerUiSettings');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
this.sendToParentLowLevel('uwui-get-role', null);
|
|
||||||
this.sendToParentLowLevel('uwui-get-theme', null);
|
|
||||||
|
|
||||||
// console.log('player overlay created — get player dims:')
|
|
||||||
this.sendToParentLowLevel('uw-bus-tunnel', {
|
|
||||||
action: 'get-player-dimensions'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribeAll(this)
|
|
||||||
},
|
|
||||||
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];
|
|
||||||
this.siteSettings = this.settings.getSiteSettings(this.site);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles trigger zone
|
|
||||||
*/
|
|
||||||
handleTriggerZone(mouseInside) {
|
|
||||||
// this.triggerZoneActive = mouseInside;
|
|
||||||
},
|
|
||||||
|
|
||||||
acknowledgeNewFeature(featureKey) {
|
|
||||||
this.settings.active.newFeatureTracker[featureKey].show = 0;
|
|
||||||
this.settings.active.newFeatureTracker[featureKey].acknowledged = true;
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
},
|
|
||||||
newFeatureViewUpdate(featureKey) {
|
|
||||||
if (!this.settings.active.newFeatureTracker[featureKey]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.settings.active.newFeatureTracker[featureKey].show--;
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
|
|
||||||
if (this.settings.active.newFeatureTracker[featureKey]?.show < 0) {
|
|
||||||
this.acknowledgeNewFeature(featureKey);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends message to parent _without_ using event bus.
|
|
||||||
*/
|
|
||||||
sendToParentLowLevel(action, payload, lowLevelExtras = {}) {
|
|
||||||
window.parent.postMessage(
|
|
||||||
{
|
|
||||||
action, payload, ...lowLevelExtras
|
|
||||||
},
|
|
||||||
'*'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
preventContextMenuHide() {
|
|
||||||
this.contextMenuActive = true;
|
|
||||||
|
|
||||||
// refresh DRM status
|
|
||||||
this.eventBus.sendToTunnel('get-drm-status');
|
|
||||||
},
|
|
||||||
allowContextMenuHide() {
|
|
||||||
this.contextMenuActive = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
setTriggerZoneActive(active, event) {
|
|
||||||
this.triggerZoneActive = active;
|
|
||||||
},
|
|
||||||
|
|
||||||
showUwWindow(tab) {
|
|
||||||
this.defaultWindowTab = tab; // can be undefined
|
|
||||||
|
|
||||||
this.uwWindowFadeOut = false;
|
|
||||||
this.uwWindowVisible = true;
|
|
||||||
this.uwTriggerZoneVisible = false;
|
|
||||||
this.allowContextMenuHide();
|
|
||||||
|
|
||||||
// refresh DRM status
|
|
||||||
this.eventBus.sendToTunnel('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.context,
|
|
||||||
borderCrossings: {
|
|
||||||
...payload.context?.borderCrossings,
|
|
||||||
iframe: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateConfig() {
|
|
||||||
this.settings.init();
|
|
||||||
this.$nextTick( () => this.$forceUpdate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.ard-blocked {
|
|
||||||
color: rgb(219, 125, 48) !important;
|
|
||||||
background-color: rgba(0,0,0,0.85) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped module></style>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
.uw-hover {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 999999999999999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reduced-padding {
|
|
||||||
padding: 1rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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: fixed;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.gib-bg {
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
backdrop-filter: blur(16px) saturate(120%);
|
|
||||||
|
|
||||||
width: fit-content;
|
|
||||||
block-size: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-warning {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
transform: translateY(-100%);
|
|
||||||
|
|
||||||
max-width: 16rem;
|
|
||||||
width: 16rem;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-word;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.context-spawn {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100dvw;
|
|
||||||
height: 100dvh;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
// width: 100%;
|
|
||||||
// height: 100%;
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
// .context-item {
|
|
||||||
// font-size: .95rem;
|
|
||||||
// padding: 1rem 1.6rem;
|
|
||||||
// background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
// backdrop-filter: blur(16px) saturate(120%);
|
|
||||||
|
|
||||||
// white-space: nowrap;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .spawn-container {
|
|
||||||
// border: 1px solid white;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.extension-status-messages {
|
|
||||||
z-index: 1000;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
font-size: 0.9rem;
|
|
||||||
|
|
||||||
// width: 112.25%;
|
|
||||||
// transform: translate(-12.5%, 12.5%) scale(0.75);
|
|
||||||
|
|
||||||
> * {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-reverse-order {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.aard-blocked {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger-zone-preview {
|
|
||||||
border: 4px solid #fa4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.debug-1 {
|
|
||||||
border: 1px solid yellow;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rbba(255,255,0,0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.debug-2 {
|
|
||||||
border: 1px solid blue;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rbba(0,0,255,.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,596 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="popup-panel" style="height: 100vh">
|
|
||||||
<!--
|
|
||||||
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"
|
|
||||||
style="height: 100vh"
|
|
||||||
class="popup flex flex-col no-overflow"
|
|
||||||
:class="{'popup-chrome': ! BrowserDetect?.firefox}"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col w-full relative header"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row w-full" style="height: 42px">
|
|
||||||
<h1 class="flex-grow">
|
|
||||||
<span class="smallcaps">Ultrawidify</span>: <small>Quick settings</small>
|
|
||||||
</h1>
|
|
||||||
<button
|
|
||||||
class="settings-header-button"
|
|
||||||
style="align-self: stretch"
|
|
||||||
@click="showInPlayerUi()"
|
|
||||||
>
|
|
||||||
Show settings window
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row w-full">
|
|
||||||
<div v-if="site && siteSettings" style="transform: scale(0.75) translateX(-12.5%); margin-bottom: -0.5rem; align-content: center" class="flex flex-row flex-grow items-center">
|
|
||||||
<div>site: {{site.host}}</div>
|
|
||||||
<SupportLevelIndicator
|
|
||||||
:siteSupportLevel="siteSupportLevel"
|
|
||||||
>
|
|
||||||
</SupportLevelIndicator>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Version info -->
|
|
||||||
<div v-if="BrowserDetect?.processEnvChannel !== 'stable'" class="absolute channel-info version-info">
|
|
||||||
<label>Version:</label> <br/>
|
|
||||||
{{ settings.getExtensionVersion() }} (non-stable)
|
|
||||||
</div>
|
|
||||||
<div v-else class="version-info">
|
|
||||||
<label>Version:</label> <br/>
|
|
||||||
{{ settings.getExtensionVersion() }}
|
|
||||||
</div>
|
|
||||||
</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,
|
|
||||||
'highlight-tab': tab.highlight,
|
|
||||||
}"
|
|
||||||
@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 window-content" style="flex: 7 7; padding: 1rem;">
|
|
||||||
<template v-if="settings && siteSettings">
|
|
||||||
<PopupVideoSettings
|
|
||||||
v-if="selectedTab === 'videoSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:hosts="activeHosts"
|
|
||||||
></PopupVideoSettings>
|
|
||||||
<BaseExtensionSettings
|
|
||||||
v-if="selectedTab === 'extensionSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:site="site.host"
|
|
||||||
:hosts="activeHosts"
|
|
||||||
>
|
|
||||||
</BaseExtensionSettings>
|
|
||||||
<ChangelogPanel
|
|
||||||
v-if="selectedTab === 'changelog'"
|
|
||||||
:settings="settings"
|
|
||||||
></ChangelogPanel>
|
|
||||||
<AboutPanel
|
|
||||||
v-if="selectedTab === 'about'"
|
|
||||||
>
|
|
||||||
</AboutPanel>
|
|
||||||
</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 ChangelogPanel from './src/PlayerUiPanels/ChangelogPanel.vue'
|
|
||||||
import PopupVideoSettings from './src/popup/panels/PopupVideoSettings.vue'
|
|
||||||
import AboutPanel from '@csui/src/popup/panels/AboutPanel.vue'
|
|
||||||
import Debug from '../ext/conf/Debug';
|
|
||||||
import BrowserDetect from '../ext/conf/BrowserDetect';
|
|
||||||
import CommsClient, {CommsOrigin} from '../ext/lib/comms/CommsClient';
|
|
||||||
import Settings from '../ext/lib/Settings';
|
|
||||||
import EventBus from '../ext/lib/EventBus';
|
|
||||||
import {ChromeShittinessMitigations as CSM} from '../common/js/ChromeShittinessMitigations';
|
|
||||||
import SupportLevelIndicator from '@csui/src/components/SupportLevelIndicator.vue'
|
|
||||||
import { LogAggregator } from '@src/ext/lib/logging/LogAggregator';
|
|
||||||
import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Debug,
|
|
||||||
BrowserDetect,
|
|
||||||
PopupVideoSettings,
|
|
||||||
PlayerDetectionPanel,
|
|
||||||
BaseExtensionSettings,
|
|
||||||
SupportLevelIndicator,
|
|
||||||
ChangelogPanel,
|
|
||||||
AboutPanel
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
comms: undefined,
|
|
||||||
eventBus: new EventBus(),
|
|
||||||
settings: {},
|
|
||||||
settingsInitialized: false,
|
|
||||||
narrowPopup: null,
|
|
||||||
sideMenuVisible: null,
|
|
||||||
logAggregator: undefined,
|
|
||||||
logger: undefined,
|
|
||||||
site: undefined,
|
|
||||||
siteSettings: undefined,
|
|
||||||
selectedTab: 'videoSettings',
|
|
||||||
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' },
|
|
||||||
{id: 'changelog', label: 'What\'s new', icon: 'alert-decagram' },
|
|
||||||
{id: 'about', label: 'About', icon: 'information-outline'},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
siteSupportLevel() {
|
|
||||||
return (this.site && this.siteSettings) ? this.siteSettings.data.type || 'no-support' : 'waiting';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.tabs.find(x => x.id === 'changelog').highlight = !this.settings.active?.whatsNewChecked;
|
|
||||||
this.requestSite();
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
try {
|
|
||||||
this.logAggregator = new LogAggregator('🔵ext-popup🔵');
|
|
||||||
this.logger = new ComponentLogger(this.logAggregator, 'Popup');
|
|
||||||
|
|
||||||
this.settings = new Settings({afterSettingsSaved: () => this.updateConfig(), logAggregator: this.logAggregator});
|
|
||||||
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',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config, context) => {
|
|
||||||
console.log('set-current-site | this.site:', this.site, 'config.site:', config.site);
|
|
||||||
|
|
||||||
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.loadHostnames();
|
|
||||||
this.loadFrames();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'open-popup-settings',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config) => {
|
|
||||||
this.selectTab(config.tab)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[Popup.vue::created()] An error happened:', e)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
showInPlayerUi() {
|
|
||||||
this.eventBus.send('uw-set-ui-state', {globalUiVisible: true}, {comms: {forwardTo: 'active'}});
|
|
||||||
},
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
isDefaultFrame(frameId) {
|
|
||||||
return frameId === '__playing' || frameId === '__all';
|
|
||||||
},
|
|
||||||
loadHostnames() {
|
|
||||||
this.activeHosts = this.site.hostnames;
|
|
||||||
},
|
|
||||||
loadFrames() {
|
|
||||||
this.activeFrames = [{
|
|
||||||
host: this.site.host,
|
|
||||||
isIFrame: false, // not used tho. Maybe one day
|
|
||||||
}];
|
|
||||||
|
|
||||||
for (const frame in this.site.frames) {
|
|
||||||
if (!this.activeFrames.find(x => x.host === this.site.frames[frame].host)) {
|
|
||||||
this.activeFrames.push({
|
|
||||||
id: `${this.site.id}-${frame}`,
|
|
||||||
label: this.site.frames[frame].host,
|
|
||||||
host: this.site.frames[frame].host,
|
|
||||||
...this.site.frames[frame],
|
|
||||||
...this.settings.active.sites[this.site.frames[frame].host]
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getRandomColor() {
|
|
||||||
return `rgb(${Math.floor(Math.random() * 128)}, ${Math.floor(Math.random() * 128)}, ${Math.floor(Math.random() * 128)})`;
|
|
||||||
},
|
|
||||||
updateConfig() {
|
|
||||||
this.settings.init();
|
|
||||||
this.$nextTick( () => this.$forceUpdate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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);
|
|
||||||
background-color: rgb(0,0,0);
|
|
||||||
color: #fff;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: row;
|
|
||||||
// justify-content: space-between;
|
|
||||||
|
|
||||||
border-bottom: 1px dotted #fa6;
|
|
||||||
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version-info {
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.8;
|
|
||||||
label {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-header-button {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
padding: 0.5rem 2rem;
|
|
||||||
text-transform: lowercase;
|
|
||||||
font-variant: small-caps;
|
|
||||||
|
|
||||||
background-color: #000;
|
|
||||||
border: 1px solid #fa68;
|
|
||||||
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
border: 1px solid #222 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: 0; padding: 0; font-weight: 400; font-size:24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-content {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,16 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en"; style="position: relative">
|
|
||||||
<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 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import GlobalFrame from './GlobalFrame';
|
|
||||||
import mdiVue from 'mdi-vue/v3';
|
|
||||||
import * as mdijs from '@mdi/js';
|
|
||||||
|
|
||||||
import './src/res-common/common.scss';
|
|
||||||
|
|
||||||
// NOTE — this is in-player interface for ultrawidify
|
|
||||||
// it is injected into the page in UI.init()
|
|
||||||
|
|
||||||
createApp(GlobalFrame)
|
|
||||||
.use(mdiVue, {icons: mdijs})
|
|
||||||
.mount('#app');
|
|
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" style="position: relative">
|
|
||||||
<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" style="position: relative">
|
|
||||||
<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" style="position: relative">
|
|
||||||
<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" style="position: relative; height: 600px">
|
|
||||||
<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: 100vh; overflow-x: hidden; min-width: 750px;"
|
|
||||||
>
|
|
||||||
<div id="app" style="max-height: 100vh;">
|
|
||||||
|
|
||||||
</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,13 +0,0 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import PlayerOverlay from './PlayerOverlay';
|
|
||||||
import mdiVue from 'mdi-vue/v3';
|
|
||||||
import * as mdijs from '@mdi/js';
|
|
||||||
|
|
||||||
import './src/res-common/common.scss';
|
|
||||||
|
|
||||||
// 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,91 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div>Ultrawidify</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div v-if="notification.icon">
|
|
||||||
<mdicon :name="notification.icon"></mdicon>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-grow flex-col">
|
|
||||||
<div v-if="notification.title">{{notification.title}}</div>
|
|
||||||
<div v-if="notification.description">{{notification.description}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="notification.actions" class="flex flex-row flex-end">
|
|
||||||
<button
|
|
||||||
v-for="(action, index) of notification.actions"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
{{ action.label }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>Notification countdown</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
notification: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
this.logger = new Logger();
|
|
||||||
|
|
||||||
window.addEventListener('message', this.handleMessage);
|
|
||||||
this.sendToParentLowLevel('init-complete', {});
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
window.removeEventListener('message', this.handleMessage);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* 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 'notification-data':
|
|
||||||
this.notification = event.data.payload;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends message to parent _without_ using event bus.
|
|
||||||
*/
|
|
||||||
sendToParentLowLevel(action, payload, lowLevelExtras = {}) {
|
|
||||||
window.parent.postMessage(
|
|
||||||
{
|
|
||||||
action, payload, ...lowLevelExtras
|
|
||||||
},
|
|
||||||
'*'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.ard-blocked {
|
|
||||||
color: rgb(219, 125, 48) !important;
|
|
||||||
background-color: rgba(0,0,0,0.85) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="../../src/res-common/common.scss" scoped module></style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" style="position: relative">
|
|
||||||
<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-notification.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import Notification from './Notification';
|
|
||||||
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(Notification)
|
|
||||||
.use(mdiVue, {icons: mdijs})
|
|
||||||
.mount('#app');
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
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: 273 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1,437 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="popup-panel flex flex-col uw-clickable h-full main-window relative"
|
|
||||||
>
|
|
||||||
<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 flex flex-row">
|
|
||||||
<div class="site-support-site">{{site}}</div>
|
|
||||||
<SupportLevelIndicator
|
|
||||||
v-if="inPlayer"
|
|
||||||
:siteSupportLevel="siteSupportLevel"
|
|
||||||
>
|
|
||||||
</SupportLevelIndicator>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="header-buttons">
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="!tab.hidden"
|
|
||||||
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>
|
|
||||||
<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>
|
|
||||||
<PlayerUiSettings
|
|
||||||
v-if="selectedTab === 'playerUiSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
>
|
|
||||||
</PlayerUiSettings>
|
|
||||||
<BaseExtensionSettings
|
|
||||||
v-if="selectedTab === 'extensionSettings'"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:site="site"
|
|
||||||
:enableSettingsEditor="true"
|
|
||||||
></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>
|
|
||||||
<!-- <ResetBackupPanel
|
|
||||||
v-if="selectedTab === 'resetBackup'"
|
|
||||||
:settings="settings"
|
|
||||||
>
|
|
||||||
</ResetBackupPanel> -->
|
|
||||||
</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 '@csui/src/PlayerUiPanels/AboutPanel.vue'
|
|
||||||
import PlayerUiSettings from './PlayerUiPanels/PlayerUiSettings.vue'
|
|
||||||
import ResetBackupPanel from './PlayerUiPanels/ResetBackupPanel.vue'
|
|
||||||
import SupportLevelIndicator from '@csui/src/components/SupportLevelIndicator.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
VideoSettings,
|
|
||||||
PlayerDetectionPanel,
|
|
||||||
BaseExtensionSettings,
|
|
||||||
AutodetectionSettingsPanel,
|
|
||||||
DebugPanel,
|
|
||||||
PlayerUiSettings,
|
|
||||||
ChangelogPanel,
|
|
||||||
AboutPanel,
|
|
||||||
SupportLevelIndicator,
|
|
||||||
ResetBackupPanel,
|
|
||||||
},
|
|
||||||
mixins: [],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
statusFlags: {
|
|
||||||
hasDrm: undefined,
|
|
||||||
},
|
|
||||||
|
|
||||||
tabs: [
|
|
||||||
// {id: 'videoSettings', label: 'Video settings', icon: 'crop'},
|
|
||||||
{id: 'extensionSettings', label: 'Site and Extension options', icon: 'cogs' },
|
|
||||||
{id: 'playerUiSettings', label: 'UI and keyboard', icon: 'movie-cog-outline' },
|
|
||||||
{id: 'playerDetection', label: 'Player detection', icon: 'television-play'},
|
|
||||||
{id: 'autodetectionSettings', label: 'Autodetection options', icon: 'auto-fix'},
|
|
||||||
// {id: 'advancedOptions', label: 'Advanced options', icon: 'cogs' },
|
|
||||||
{id: 'changelog', label: 'What\'s new', icon: 'alert-decagram' },
|
|
||||||
{id: 'about', label: 'About', icon: 'information-outline'},
|
|
||||||
{id: 'debugging', label: 'Debugging', icon: 'bug-outline', hidden: true},
|
|
||||||
],
|
|
||||||
selectedTab: 'extensionSettings',
|
|
||||||
BrowserDetect: BrowserDetect,
|
|
||||||
preventClose: false,
|
|
||||||
siteSettings: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'eventBus',
|
|
||||||
'logger',
|
|
||||||
'in-player',
|
|
||||||
'site',
|
|
||||||
'defaultTab'
|
|
||||||
],
|
|
||||||
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.settings.listenAfterChange(this.setDebugTabVisibility);
|
|
||||||
|
|
||||||
if (this.defaultTab) {
|
|
||||||
this.selectedTab = this.defaultTab;
|
|
||||||
}
|
|
||||||
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',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: () => {
|
|
||||||
if (this.inPlayer) {
|
|
||||||
return; // show-ui is only intended for global overlay
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
this.setDebugTabVisibility();
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.settings.removeListenerAfterChange(this.setDebugTabVisibility);
|
|
||||||
this.eventBus.unsubscribeAll(this);
|
|
||||||
},
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
setDebugTabVisibility() {
|
|
||||||
const debugTab = this.tabs.find( x => x.id === 'debugging');
|
|
||||||
if (debugTab) {
|
|
||||||
debugTab.hidden = !this.settings.active.ui.devMode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.site-support-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: bottom;
|
|
||||||
|
|
||||||
.site-support-site {
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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: 6rem;
|
|
||||||
|
|
||||||
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,122 +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>
|
|
||||||
Please 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>
|
|
||||||
<p>
|
|
||||||
I also have <a href="https://instagram.com/shaman_urkog" target="_blank">instagram with nerdy shit</a> <small>(mini painting + various fantasy events)</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,179 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="alignment-box" :class="{large: large}">
|
|
||||||
<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
|
|
||||||
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
|
|
||||||
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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: [
|
|
||||||
'eventBus',
|
|
||||||
'large',
|
|
||||||
],
|
|
||||||
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: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
&.large {
|
|
||||||
max-width: 15rem;
|
|
||||||
|
|
||||||
.col {
|
|
||||||
width: 4rem;
|
|
||||||
height: 4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.col {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
background-color: rgba(0,0,0,0.25);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
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,546 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col tab-root">
|
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
|
|
||||||
<!-- AARD performance metrics -->
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h1>Automatic Aspect Ratio Detection</h1>
|
|
||||||
</div>
|
|
||||||
<div class="aard-settings-group">
|
|
||||||
|
|
||||||
<!-- the last time i tried to comment out this block, it didn't work properly — v-if="false" it is -->
|
|
||||||
<div v-if="false">
|
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="settings-segment">
|
|
||||||
<!-- <h2>Basic settings</h2> -->
|
|
||||||
|
|
||||||
<!-- <div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Stop autodetection after first detection:
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<input type="checkbox" v-model="settings.active.arDetect.earlyStopOptions.stopAfterFirstDetection" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Stop detection after a period of time:
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<input type="checkbox" v-model="settings.active.arDetect.earlyStopOptions.stopAfterTimeout" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Stop detection after:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input type="input" v-model="settings.active.arDetect.earlyStopOptions.stopTimeout" />
|
|
||||||
<div class="unit">seconds</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Autodetection frequency (time between samples)</div>
|
|
||||||
<div class="range-input">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
:value="Math.log(settings.active.arDetect.timers.playing)"
|
|
||||||
@change="setArCheckFrequency($event.target.value)"
|
|
||||||
min="2.3"
|
|
||||||
max="9.3"
|
|
||||||
step="0.01"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
v-model="settings.active.arDetect.timers.playing"
|
|
||||||
@change="setArCheckFrequency($event.target.value)"
|
|
||||||
class="input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
<div class="unit">ms</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Frame extraction canvas type:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select v-model="settings.active.arDetect.aardType" @change="settings.saveWithoutReload">
|
|
||||||
<option value="auto">Automatic</option>
|
|
||||||
<option value="webgl">WebGL only</option>
|
|
||||||
<option value="legacy">Legacy / fallback</option>
|
|
||||||
</select>
|
|
||||||
</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="hint">
|
|
||||||
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 (% of total height).
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="settings.active.ui.devMode" class="settings-segment">
|
|
||||||
<p>
|
|
||||||
<b>Debug options</b>
|
|
||||||
</p>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<button @click="eventBus.sendToTunnel('aard-enable-debug', true)">Show debug overlay</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="label">Show debug overlay on startup</div>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
v-model="settings.active.ui.dev.aardDebugOverlay.showOnStartup"
|
|
||||||
@change="settings.saveWithoutReload"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<JsonEditor
|
|
||||||
v-model="settingsJson"
|
|
||||||
>
|
|
||||||
</JsonEditor>
|
|
||||||
<button @click="saveDebugUiSettings">Save debug UI settings</button>
|
|
||||||
</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';
|
|
||||||
import JsonEditor from '@csui/src/components/JsonEditor';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
Button,
|
|
||||||
AlignmentOptionsControlComponent,
|
|
||||||
JsonEditor
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'frame',
|
|
||||||
'eventBus',
|
|
||||||
'site'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
exec: null,
|
|
||||||
performanceData: {},
|
|
||||||
graphRefreshInterval: undefined,
|
|
||||||
settingsJson: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'uw-config-broadcast',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config) => this.handleConfigBroadcast(config)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.eventBus.sendToTunnel('get-aard-timing');
|
|
||||||
this.graphRefreshInterval = setInterval(() => this.eventBus.sendToTunnel('get-aard-timing'), 500);
|
|
||||||
this.resetSettingsEditor();
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribeAll(this);
|
|
||||||
clearInterval(this.graphRefreshInterval);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async openOptionsPage() {
|
|
||||||
BrowserDetect.runtime.openOptionsPage();
|
|
||||||
},
|
|
||||||
setArCheckFrequency(event) {
|
|
||||||
this.settings.active.arDetect.timers.playing = Math.floor(Math.pow(Math.E, event));
|
|
||||||
},
|
|
||||||
refreshGraph() {
|
|
||||||
this.eventBus.sendToTunnel('get-aard-timing');
|
|
||||||
},
|
|
||||||
handleConfigBroadcast(data) {
|
|
||||||
if (data.type === 'aard-performance-data') {
|
|
||||||
this.performanceData = data.performanceData;
|
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetSettingsEditor() {
|
|
||||||
this.settingsJson = JSON.parse(JSON.stringify(this.settings?.active.ui.dev.aardDebugOverlay ?? {}));
|
|
||||||
},
|
|
||||||
saveDebugUiSettings() {
|
|
||||||
this.settings.active.ui.dev.aardDebugOverlay = JSON.parse(JSON.stringify(this.settingsJson));
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped module></style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped module>
|
|
||||||
@import '../res-common/variables';
|
|
||||||
|
|
||||||
// .aard-settings-group {
|
|
||||||
// max-width: 69rem;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.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,315 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-row w-full h-full">
|
|
||||||
<div class="flex flex-col w-full">
|
|
||||||
|
|
||||||
<!-- TAB ROW -->
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div
|
|
||||||
class="tab"
|
|
||||||
:class="{'active': tab === 'siteSettings'}"
|
|
||||||
@click="setTab('siteSettings')"
|
|
||||||
>
|
|
||||||
Current site<br/>
|
|
||||||
<small>{{ site }}</small>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="hosts"
|
|
||||||
class="tab"
|
|
||||||
:class="{'active': tab === 'embeddedSites'}"
|
|
||||||
@click="setTab(tab = 'embeddedSites')"
|
|
||||||
>
|
|
||||||
Embedded content ({{hosts?.length}} {{hosts?.length === 1 ? 'site' : 'sites'}})
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab"
|
|
||||||
:class="{'active': tab === 'otherSites'}"
|
|
||||||
@click="setTab(tab = 'otherSites')"
|
|
||||||
>
|
|
||||||
Defaults & other sites
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-if="tab === 'siteSettings' && siteSettings">
|
|
||||||
<SiteExtensionSettings
|
|
||||||
v-if="settings"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:isDefaultConfiguration="false"
|
|
||||||
></SiteExtensionSettings>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="hosts && tab === 'embeddedSites' && globalSettings">
|
|
||||||
<FrameSiteSettings
|
|
||||||
v-if="settings"
|
|
||||||
:hosts="hosts"
|
|
||||||
:settings="settings"
|
|
||||||
></FrameSiteSettings>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="tab === 'otherSites'">
|
|
||||||
<OtherSiteSettings
|
|
||||||
v-if="settings"
|
|
||||||
:settings="settings"
|
|
||||||
>
|
|
||||||
</OtherSiteSettings>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Reset options -->
|
|
||||||
<div class="flex flex-col" style="margin-top: 2rem">
|
|
||||||
<h2>Reset and backup</h2>
|
|
||||||
<p><small>Settings import must be done from in-player UI.</small></p>
|
|
||||||
<div class="flex flex-row w-full">
|
|
||||||
<UploadJsonFileButton
|
|
||||||
class="flex-grow"
|
|
||||||
@importedJson="handleImportedSettings"
|
|
||||||
@error="handleSettingsImportError"
|
|
||||||
>
|
|
||||||
Import settings
|
|
||||||
</UploadJsonFileButton>
|
|
||||||
<Popup
|
|
||||||
v-if="importSettingDialogConfig.visible"
|
|
||||||
title="Overwrite existing settings?"
|
|
||||||
message="Importing settings from a file will overwrite existing settings. Continue?"
|
|
||||||
confirmButtonText="Import settings"
|
|
||||||
cancelButtonText="Cancel"
|
|
||||||
@onConfirm="importSettingDialogConfig.confirm"
|
|
||||||
@onCancel="importSettingDialogConfig.reject"
|
|
||||||
>
|
|
||||||
</Popup>
|
|
||||||
<button class="flex-grow" @click="exportSettings()">Export settings</button>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<ConfirmButton
|
|
||||||
dialogType="danger"
|
|
||||||
@onConfirmed="resetSettings"
|
|
||||||
>
|
|
||||||
Reset settings
|
|
||||||
</ConfirmButton>
|
|
||||||
|
|
||||||
<div v-if="enableSettingsEditor" class="field">
|
|
||||||
<div class="label">Show developer options</div>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
v-model="settings.active.ui.devMode"
|
|
||||||
@change="settings.saveWithoutReload"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="enableSettingsEditor && settings.active.ui.devMode" class="h-full grow">
|
|
||||||
<h2>Settings editor</h2>
|
|
||||||
<div class="flex flex-row w-full">
|
|
||||||
<div class="flex flex-row items-center">
|
|
||||||
<div>Enable save button:</div>
|
|
||||||
<input v-model="allowSettingsEditing" type="checkbox">
|
|
||||||
</div>
|
|
||||||
<div class="grow">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div v-if="editorSaveFinished">Settings saved ...</div>
|
|
||||||
<button v-else
|
|
||||||
class="danger"
|
|
||||||
:class="{'disabled': !allowSettingsEditing}"
|
|
||||||
:disabled="!allowSettingsEditing"
|
|
||||||
@click="() => saveSettingsChanges()"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button @click="resetSettingsEditor">
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<JsonEditor
|
|
||||||
v-model="settingsJson"
|
|
||||||
>
|
|
||||||
</JsonEditor>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Settings snapshots</h2>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div v-for="(snapshot, index) of settingsSnapshots" :key="snapshot.createdAt">
|
|
||||||
<small>{{new Date(snapshot.createdAt).toISOString()}}</small>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div class="grow">
|
|
||||||
{{snapshot.name}}
|
|
||||||
</div>
|
|
||||||
<div v-if="settings.isAutomatic">(auto)</div>
|
|
||||||
<div v-if="settings.isAutomatic && settings.isProtected">(protected)</div>
|
|
||||||
<div v-if="settings.default">(default)</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button @click="() => markDefaultSnapshot(index)"><template v-if="settings.isDefault">Revoke default</template><template v-else>Make default</template></button>
|
|
||||||
<button v-if="settings.isAutomatic" @click="() => toggleSnapshotProtection(index)">Toggle protection</button>
|
|
||||||
<button @click="() => deleteSnapshot(index)">Delete snapshot</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SiteExtensionSettings from './PanelComponents/ExtensionSettings/SiteExtensionSettings.vue';
|
|
||||||
import FrameSiteSettings from './PanelComponents/ExtensionSettings/FrameSiteSettings.vue';
|
|
||||||
import OtherSiteSettings from './PanelComponents/ExtensionSettings/OtherSiteSettings.vue';
|
|
||||||
import Popup from '@csui/src/components/Popup';
|
|
||||||
import ConfirmButton from '@csui/src/components/ConfirmButton';
|
|
||||||
import UploadJsonFileButton from '@csui/src/components/UploadJsonFileButton';
|
|
||||||
import JsonEditor from '@csui/src/components/JsonEditor';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
|
|
||||||
components: {
|
|
||||||
SiteExtensionSettings,
|
|
||||||
OtherSiteSettings,
|
|
||||||
Popup,
|
|
||||||
ConfirmButton,
|
|
||||||
UploadJsonFileButton,
|
|
||||||
JsonEditor,
|
|
||||||
FrameSiteSettings,
|
|
||||||
},
|
|
||||||
mixins: [],
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'site',
|
|
||||||
'enableSettingsEditor',
|
|
||||||
'hosts',
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
tab: 'siteSettings',
|
|
||||||
importSettingDialogConfig: {visible: false},
|
|
||||||
allowSettingsEditing: false,
|
|
||||||
editorSaveFinished: false,
|
|
||||||
settingsJson: {},
|
|
||||||
settingsSnapshots: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
globalSettings() {
|
|
||||||
return this.settings?.getSiteSettings('@global') ?? null;
|
|
||||||
},
|
|
||||||
siteSettings() {
|
|
||||||
if (this.site) {
|
|
||||||
return this.settings?.getSiteSettings(this.site) ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.resetSettingsEditor();
|
|
||||||
this.loadSettingsSnapshots();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setTab(tab) {
|
|
||||||
this.tab = tab;
|
|
||||||
},
|
|
||||||
|
|
||||||
//#region settings management
|
|
||||||
/**
|
|
||||||
* Exports extension settings into a json file.
|
|
||||||
*/
|
|
||||||
exportSettings() {
|
|
||||||
const settingBlob = new Blob(
|
|
||||||
[ JSON.stringify(this.settings.active, null, 2) ],
|
|
||||||
{ type: "application/json"}
|
|
||||||
);
|
|
||||||
const url = window.URL.createObjectURL(settingBlob);
|
|
||||||
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = url;
|
|
||||||
a.download = "ultrawidify-settings.json";
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleImportedSettings(newSettings) {
|
|
||||||
this.importSettingDialogConfig = {
|
|
||||||
visible: true,
|
|
||||||
confirm: () => {
|
|
||||||
this.settings.active = newSettings;
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
this.importSettingDialogConfig = {visible: false};
|
|
||||||
this.resetSettingsEditor();
|
|
||||||
},
|
|
||||||
reject: () => {
|
|
||||||
this.importSettingDialogConfig = {visible: false};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSettingsImportError(error) {
|
|
||||||
console.error('Error importing settings:', error);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets settings to default
|
|
||||||
*/
|
|
||||||
resetSettings() {
|
|
||||||
this.settings.active = JSON.parse(JSON.stringify(this.settings.default));
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
this.resetSettingsEditor();
|
|
||||||
},
|
|
||||||
|
|
||||||
async saveSettingsChanges() {
|
|
||||||
if (this.allowSettingsEditing) {
|
|
||||||
const currentVersion = this.settings.active?.version;
|
|
||||||
this.settings.active = this.settingsJson;
|
|
||||||
|
|
||||||
if (currentVersion !== this.settingsJson.version) {
|
|
||||||
await this.settings.save({forcePreserveVersion: true});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
await this.settings.saveWithoutReload();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resetSettingsEditor();
|
|
||||||
this.editorSaveFinished = true;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.editorSaveFinished = false;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
resetSettingsEditor() {
|
|
||||||
this.settingsJson = JSON.parse(JSON.stringify(this.settings?.active ?? {}));
|
|
||||||
},
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region settings snapshot management
|
|
||||||
async loadSettingsSnapshots() {
|
|
||||||
this.settingsSnapshots = await this.settings.snapshotManager.listSnapshots();
|
|
||||||
},
|
|
||||||
|
|
||||||
async markDefaultSnapshot(index) {
|
|
||||||
await this.settings.snapshotManager.setDefaultSnapshot(index, !this.settingsSnapshots[index].isDefault);
|
|
||||||
await this.loadSettingsSnapshots();
|
|
||||||
this.settings.active.dev.loadFromSnapshot = this.settingsSnapshots[index].isDefault;
|
|
||||||
this.saveSettingsChanges();
|
|
||||||
},
|
|
||||||
|
|
||||||
async toggleSnapshotProtection(index) {
|
|
||||||
await this.settings.snapshotManager.setSnapshotAsProtected(index, !this.settingsSnapshots[index].isProtected);
|
|
||||||
await this.loadSettingsSnapshots();
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteSnapshot(index) {
|
|
||||||
await this.settings.snapshotManager.deleteSnapshot(index);
|
|
||||||
await this.loadSettingsSnapshots();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped module></style>
|
|
@ -1,101 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col w-full h-full gap-2">
|
|
||||||
<div class="flex flex-row gap-8 bg-black flex-wrap w-full">
|
|
||||||
<div class="min-w-[400px] max-w-[520px] grow shrink">
|
|
||||||
<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.3.0</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Automatic aspect ratio detection: do not apply negative aspect ratios</li>
|
|
||||||
<li>Keyboard zoom now works</li>
|
|
||||||
<li><code>www.youtube-nocookie.com</code> has been added to the "officially supported" list</li>
|
|
||||||
<li>Fixed the bug where UI would sometimes refuse to stay hidden</li>
|
|
||||||
<li>New experimental zoom options</li>
|
|
||||||
<li>Subdomains now inherit same settings as their parent domain by default</li>
|
|
||||||
<li>Extension attempts to detect embedded content.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div style="width: 1rem; height: 0px;"></div>
|
|
||||||
<div class="min-w-[400px] max-w-[520px] grow shrink">
|
|
||||||
<h2>Report a problem</h2>
|
|
||||||
<p>
|
|
||||||
Please 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 extension version, whether you installed the extension from, and description of your problem.
|
|
||||||
</p>
|
|
||||||
<p> </p>
|
|
||||||
<p> </p>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import BrowserDetect from '@src/ext/conf/BrowserDetect';
|
|
||||||
|
|
||||||
export default({
|
|
||||||
props: [
|
|
||||||
'settings'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
BrowserDetect: BrowserDetect,
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.settings.active.whatsNewChecked = true;
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grow {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,103 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h1>{{loggerPanel.title}}<small><br/>{{loggerPanel.subtitle}}</small></h1>
|
|
||||||
|
|
||||||
<div>Logger configuration:</div>
|
|
||||||
|
|
||||||
<JsonEditor
|
|
||||||
v-model="lastSettings"
|
|
||||||
></JsonEditor>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<div class="button" @click="getLoggerSettings()">
|
|
||||||
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 { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/lib/logging/LogAggregator';
|
|
||||||
import JsonEditor from '@csui/src/components/JsonEditor';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
JsonEditor
|
|
||||||
},
|
|
||||||
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.getLoggerSettings();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
loadDefaultConfig() {
|
|
||||||
this.lastSettings = JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG));
|
|
||||||
},
|
|
||||||
async getLoggerSettings() {
|
|
||||||
this.lastSettings = await LogAggregator.getConfig() || BLANK_LOGGER_CONFIG;
|
|
||||||
},
|
|
||||||
async saveLoggerSettings() {
|
|
||||||
console.log('Saving logger settings', this.lastSettings);
|
|
||||||
await LogAggregator.saveConfig({...this.lastSettings});
|
|
||||||
console.log('[ok] logger settings saved');
|
|
||||||
},
|
|
||||||
async startLogging(){
|
|
||||||
this.logStringified = undefined;
|
|
||||||
await LogAggregator.saveConfig({...this.lastSettings});
|
|
||||||
window.location.reload();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,107 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="">
|
|
||||||
<template v-if="!selectedSite">
|
|
||||||
<div style="margin: 1rem 0rem" class="w-full">
|
|
||||||
<div class="flex flex-row items-baseline">
|
|
||||||
<div style="margin-right: 1rem">Search for site:</div>
|
|
||||||
<div class="input flex-grow">
|
|
||||||
<input v-model="siteFilter" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="host of hosts" :key="host" @click="selectedSite = host" class="flex flex-col container pointer hoverable" style="margin-top: 4px; padding: 0.5rem 1rem;">
|
|
||||||
<SiteListItem
|
|
||||||
:host="host"
|
|
||||||
:settings="settings"
|
|
||||||
></SiteListItem>
|
|
||||||
</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 === '@global' ? 'default settings' : selectedSite }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<SiteExtensionSettings
|
|
||||||
v-if="selectedSiteSettings"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="selectedSiteSettings"
|
|
||||||
:isDefaultConfiguration="selectedSite === '@global'"
|
|
||||||
></SiteExtensionSettings>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SiteExtensionSettings from './SiteExtensionSettings.vue';
|
|
||||||
import SiteListItem from './SiteListItem.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
SiteExtensionSettings,
|
|
||||||
SiteListItem,
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'hosts',
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedSite: null,
|
|
||||||
siteFilter: '',
|
|
||||||
filteredSites: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
sites() {
|
|
||||||
if (!this.settings?.active?.sites) {
|
|
||||||
return [];
|
|
||||||
} else {
|
|
||||||
const sites = [];
|
|
||||||
for (const siteKey in this.settings.active.sites) {
|
|
||||||
if (!siteKey.startsWith('@') && (!this.siteFilter.trim() || siteKey.includes(this.siteFilter))) {
|
|
||||||
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: {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.hoverable {
|
|
||||||
border: 1px solid #333;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border: 1px solid #fa6;
|
|
||||||
color: rgb(255, 231, 212);
|
|
||||||
background-color: rgba(#fa6, 0.125);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,172 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="">
|
|
||||||
<template v-if="!selectedSite">
|
|
||||||
|
|
||||||
<div class="flex flex-col container pointer hoverable" style="margin-top: 1rem; padding: 0.5rem 1rem;" @click="selectedSite = '@global'" >
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div class="flex-grow pointer">
|
|
||||||
<b style="color: #fa6">Default settings</b>
|
|
||||||
<!-- <span :style="getSiteTypeColor('@global')"> -->
|
|
||||||
<!-- (config: {{site.type ?? 'unknown'}}) -->
|
|
||||||
<!-- </span> -->
|
|
||||||
</div>
|
|
||||||
<div>Edit</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<small>
|
|
||||||
<span style="text-transform: uppercase; font-size: 0.9rem">Enable extension: <span style="font-size: 0.9rem;" :style="getSiteEnabledColor('@global', 'enable')">{{ getSiteEnabledModes('@global', 'enable') }}</span></span> <br/>
|
|
||||||
Autodetection: <span :style="getSiteEnabledColor('@global', 'enableAard')"><small>{{ getSiteEnabledModes('@global', 'enableAard') }}</small></span>;
|
|
||||||
Keyboard shortcuts: <span :style="getSiteEnabledColor('@global', 'enableKeyboard')"><small>{{ getSiteEnabledModes('@global', 'enableKeyboard') }}</small></span>;
|
|
||||||
In-player UI: <span :style="getSiteEnabledColor('@global', 'enableUI')"><small>{{ getSiteEnabledModes('@global', 'enableUI') }}</small></span>;
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 1rem; margin-bottom: 1rem;">
|
|
||||||
<div class="info" style="float: none">
|
|
||||||
<b>NOTE:</b> Sites not on this list use default extension settings.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full text-center" style="margin-bottom: -1.25rem">
|
|
||||||
<b>Other sites</b>
|
|
||||||
</div>
|
|
||||||
<div style="margin: 1rem 0rem" class="w-full">
|
|
||||||
<div class="flex flex-row items-baseline">
|
|
||||||
<div style="margin-right: 1rem">Search for site:</div>
|
|
||||||
<div class="input flex-grow">
|
|
||||||
<input v-model="siteFilter" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="site of sites" :key="site.key" @click="selectedSite = site.key" class="flex flex-col container pointer hoverable" 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>
|
|
||||||
UI: <span :style="getSiteEnabledColor(site.key, 'enableUI')"><small>{{ getSiteEnabledModes(site.key, 'enableUI') }}</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 === '@global' ? 'default settings' : selectedSite }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<SiteExtensionSettings
|
|
||||||
v-if="selectedSiteSettings"
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="selectedSiteSettings"
|
|
||||||
:isDefaultConfiguration="selectedSite === '@global'"
|
|
||||||
></SiteExtensionSettings>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ExtensionMode from '../../../../../common/enums/ExtensionMode.enum';
|
|
||||||
import SiteExtensionSettings from './SiteExtensionSettings.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedSite: null,
|
|
||||||
siteFilter: '',
|
|
||||||
filteredSites: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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('@') && (!this.siteFilter.trim() || siteKey.includes(this.siteFilter))) {
|
|
||||||
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="@csui/src/res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.hoverable {
|
|
||||||
border: 1px solid #333;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border: 1px solid #fa6;
|
|
||||||
color: rgb(255, 231, 212);
|
|
||||||
background-color: rgba(#fa6, 0.125);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,607 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- Enable extension -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Enable <span class="color-emphasis">extension</span>
|
|
||||||
<span class="sub-label"><br/>under the following conditions:</span>
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="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 by default
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ({{simpleDefaultSettings.enable}})
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<!-- The rest of the menu is disabled when extension is disabled -->
|
|
||||||
<div :class="{disabled: simpleEffectiveSettings.enable === 'disabled' && !isDefaultConfiguration}">
|
|
||||||
<!-- Enable AARD -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Enable <span class="color-emphasis">automatic aspect ratio detection</span>
|
|
||||||
<span class="sub-label"><br/>under the following conditions:</span>
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="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 by default
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ({{simpleDefaultSettings.enableAard}})
|
|
||||||
</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 <span class="color-emphasis">keyboard shortcuts</span>
|
|
||||||
<span class="sub-label"><br/>under the following conditions:</span>
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="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 by default
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ({{simpleDefaultSettings.enableKeyboard}})
|
|
||||||
</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 UI -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Enable <span class="color-emphasis">in-player UI</span>
|
|
||||||
<span class="sub-label"><br/>under the following conditions:</span>
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="simpleExtensionSettings.enableUI"
|
|
||||||
@click="setExtensionMode('enableUI', $event)"
|
|
||||||
>
|
|
||||||
<template v-if="isDefaultConfiguration">
|
|
||||||
<option value="disabled">
|
|
||||||
Disabled by default
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<option value="default">
|
|
||||||
Use default ({{simpleDefaultSettings.enableUI}})
|
|
||||||
</option>
|
|
||||||
<option value="disabled">
|
|
||||||
Never
|
|
||||||
</option>
|
|
||||||
</template>
|
|
||||||
<option value="fs">
|
|
||||||
Fullscreen only
|
|
||||||
</option>
|
|
||||||
<option value="theater">
|
|
||||||
Always where possible
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Default crop -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default crop:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="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>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
|
|
||||||
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum';
|
|
||||||
import CropModePersistence from '@src/common/enums/CropModePersistence.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
CropModePersistence: CropModePersistence,
|
|
||||||
ExtensionMode,
|
|
||||||
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'),
|
|
||||||
enableUI: this.compileSimpleSettings('enableUI')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
simpleEffectiveSettings() {
|
|
||||||
return {
|
|
||||||
enable: this.compileSimpleSettings('enable', 'site-effective'),
|
|
||||||
enableAard: this.compileSimpleSettings('enableAard', 'site-effective'),
|
|
||||||
enableKeyboard: this.compileSimpleSettings('enableKeyboard', 'site-effective'),
|
|
||||||
enableUI: this.compileSimpleSettings('enableUI', 'site-effective')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
simpleDefaultSettings() {
|
|
||||||
return {
|
|
||||||
enable: this.getDefaultOptionLabel('enable'),
|
|
||||||
enableAard: this.getDefaultOptionLabel('enableAard'),
|
|
||||||
enableKeyboard: this.getDefaultOptionLabel('enableKeyboard'),
|
|
||||||
enableUI: this.getDefaultOptionLabel('enableUI')
|
|
||||||
};
|
|
||||||
},
|
|
||||||
siteDefaultCrop() {
|
|
||||||
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 '??';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.forceRefreshPage();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Compiles our extension settings into more user-friendly options
|
|
||||||
*/
|
|
||||||
compileSimpleSettings(component, getFor = 'site') {
|
|
||||||
|
|
||||||
let settingsData;
|
|
||||||
switch (getFor) {
|
|
||||||
case 'site':
|
|
||||||
settingsData = this.siteSettings?.raw;
|
|
||||||
break;
|
|
||||||
case 'site-effective':
|
|
||||||
settingsData = this.siteSettings?.data;
|
|
||||||
break;
|
|
||||||
case 'default':
|
|
||||||
settingsData = this.settings.active.sites['@global'];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('getting data from:', settingsData);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
|
|
||||||
&& settingsData?.[component]?.theater === ExtensionMode.Disabled
|
|
||||||
&& settingsData?.[component]?.fullscreen === ExtensionMode.Disabled
|
|
||||||
) {
|
|
||||||
return 'disabled';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
( settingsData?.[component]?.normal === ExtensionMode.Default || component === 'enableUI')
|
|
||||||
&& settingsData?.[component]?.theater === ExtensionMode.Default
|
|
||||||
&& settingsData?.[component]?.fullscreen === ExtensionMode.Default
|
|
||||||
) {
|
|
||||||
// console.log(
|
|
||||||
// component, 'is set to default because:\n',
|
|
||||||
// `\nsettingsData[${component}].normal: ${settingsData?.[component]?.normal} || component is enableUI?`, component,
|
|
||||||
// `\nsettingsData[${component}].theater: ${settingsData?.[component]?.normal}`,
|
|
||||||
// `\nsettingsData[${component}].fullscreen: ${settingsData?.[component]?.normal}`,
|
|
||||||
|
|
||||||
// `\n\n(expected values:`, ExtensionMode.Default
|
|
||||||
// )
|
|
||||||
return 'default';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
|
|
||||||
&& settingsData?.[component]?.theater === ExtensionMode.Disabled
|
|
||||||
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
|
|
||||||
) {
|
|
||||||
return 'fs';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
|
|
||||||
&& settingsData?.[component]?.theater === ExtensionMode.Enabled
|
|
||||||
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
|
|
||||||
) {
|
|
||||||
return 'theater';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
( settingsData?.[component]?.normal === ExtensionMode.Enabled || component === 'enableUI')
|
|
||||||
&& settingsData?.[component]?.theater === ExtensionMode.Enabled
|
|
||||||
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
|
|
||||||
) {
|
|
||||||
return 'enabled';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'complex';
|
|
||||||
} catch (e) {
|
|
||||||
return 'loading';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultOptionLabel(component) {
|
|
||||||
const componentValue = this.compileSimpleSettings(component, 'default');
|
|
||||||
|
|
||||||
if (componentValue === 'loading') {
|
|
||||||
return componentValue;
|
|
||||||
}
|
|
||||||
if (component === 'enableUI') {
|
|
||||||
switch (componentValue) {
|
|
||||||
case 'fs':
|
|
||||||
return 'fullscreen only';
|
|
||||||
case 'enabled':
|
|
||||||
case 'theater':
|
|
||||||
return 'where possible';
|
|
||||||
case 'disabled':
|
|
||||||
return 'disabled';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (componentValue) {
|
|
||||||
case 'fs':
|
|
||||||
return 'Fullscreen only';
|
|
||||||
case 'theater':
|
|
||||||
return 'theater & FS';
|
|
||||||
case 'enabled':
|
|
||||||
return 'always';
|
|
||||||
case 'disabled':
|
|
||||||
return 'disabled';
|
|
||||||
case 'default':
|
|
||||||
return 'complex'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.siteSettings.set(option, commandArguments, {reload: false});
|
|
||||||
|
|
||||||
// we also need to force re-compute all watchers, otherwise UI will lag behind
|
|
||||||
// actual state of settings until reload
|
|
||||||
this.forceRefreshPage();
|
|
||||||
},
|
|
||||||
forceRefreshPage() {
|
|
||||||
this._computedWatchers?.simpleExtensionSettings?.run();
|
|
||||||
this._computedWatchers?.simpleDefaultSettings?.run();
|
|
||||||
this._computedWatchers?.siteDefaultCrop?.run();
|
|
||||||
this._computedWatchers?.siteDefaultStretch?.run();
|
|
||||||
this._computedWatchers?.siteDefaultAlignment?.run();
|
|
||||||
this._computedWatchers?.siteDefaultCropPersistence?.run();
|
|
||||||
this._computedWatchers?.defaultPersistanceLabel?.run();
|
|
||||||
|
|
||||||
this.$nextTick( () => this.$forceUpdate());
|
|
||||||
},
|
|
||||||
|
|
||||||
setExtensionMode(component, event) {
|
|
||||||
const option = event.target.value;
|
|
||||||
|
|
||||||
console.log('SET EXTENSION MODE — OPTIONS:', option);
|
|
||||||
|
|
||||||
if (option === 'complex') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component === 'enable' && !this.isDefaultConfiguration) {
|
|
||||||
this.setExtensionMode('enableAard', event);
|
|
||||||
this.setExtensionMode('enableKeyboard', event);
|
|
||||||
|
|
||||||
// in enableUI, 'enabled' is unused and 'theater' uses its place
|
|
||||||
if (option === 'enabled') {
|
|
||||||
this.setExtensionMode('enableUI', {target: {value: 'theater'}});
|
|
||||||
} else {
|
|
||||||
this.setExtensionMode('enableUI', event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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="@csui/src/res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
||||||
<style scoped>
|
|
||||||
.button-hover:hover {
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,71 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div class="flex-grow pointer">
|
|
||||||
<b>{{ host }}</b>
|
|
||||||
<span :style="getSiteTypeColor(siteSettings?.data?.type)">
|
|
||||||
(config: {{siteSettings?.data?.type ?? 'unknown'}})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div>Edit</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="this.siteSettings?.usesSettingsFor">
|
|
||||||
<div v-if="this.siteSettings.usesSettingsFor === '@global'">Uses default settings</div>
|
|
||||||
<div v-else>Uses settings for: {{this.siteSettings.usesSettingsFor}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<small>
|
|
||||||
Enabled: <span :style="getSiteEnabledColor(host, 'enable')"><small>{{ getSiteEnabledModes(host, 'enable') }}</small></span>;
|
|
||||||
Aard <span :style="getSiteEnabledColor(host, 'enableAard')"><small>{{ getSiteEnabledModes(host, 'enableAard') }}</small></span>;
|
|
||||||
kbd: <span :style="getSiteEnabledColor(host, 'enableKeyboard')"><small>{{ getSiteEnabledModes(host, 'enableKeyboard') }}</small></span>
|
|
||||||
UI: <span :style="getSiteEnabledColor(host, 'enableUI')"><small>{{ getSiteEnabledModes(host, 'enableUI') }}</small></span>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import ExtensionMode from '../../../../../common/enums/ExtensionMode.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
siteSettings: undefined,
|
|
||||||
supportType: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings',
|
|
||||||
'host',
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.siteSettings = this.settings.getSiteSettings(this.host);
|
|
||||||
},
|
|
||||||
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.siteSettings?.normal === ExtensionMode.Enabled) {
|
|
||||||
return 'always';
|
|
||||||
}
|
|
||||||
if (this.siteSettings?.data[component]?.theater === ExtensionMode.Enabled) {
|
|
||||||
return 'T + FS';
|
|
||||||
}
|
|
||||||
if (this.siteSettings?.data[component]?.fullscreen === ExtensionMode.Enabled) {
|
|
||||||
return 'fullscreen';
|
|
||||||
}
|
|
||||||
return 'disabled';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,250 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="sub-panel-content flex flex-row flex-wrap">
|
|
||||||
|
|
||||||
<ShortcutButton
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
class="flex button"
|
|
||||||
:class="{
|
|
||||||
active: editMode ? index === editModeOptions?.crop?.selectedIndex : isActiveCrop(command),
|
|
||||||
'b3-compact': compact,
|
|
||||||
b3: !compact
|
|
||||||
}"
|
|
||||||
: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>
|
|
||||||
<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 class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.crop.selected.label">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</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>
|
|
||||||
<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 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 v-if="siteSettings && allowSettingSiteDefault" 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 '@csui/src/components/ShortcutButton.vue';
|
|
||||||
import EditShortcutButton from '@csui/src/components/EditShortcutButton';
|
|
||||||
import EditModeMixin from '@csui/src/utils/EditModeMixin';
|
|
||||||
import KeyboardShortcutParserMixin from '@csui/src/utils/KeyboardShortcutParserMixin';
|
|
||||||
import CommsMixin from '@csui/src/utils/CommsMixin';
|
|
||||||
import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
// ComputeActionsMixin,
|
|
||||||
EditModeMixin,
|
|
||||||
KeyboardShortcutParserMixin,
|
|
||||||
CommsMixin
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
AspectRatioType: AspectRatioType,
|
|
||||||
|
|
||||||
// TODO: this should be mixin?
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
'isEditing',
|
|
||||||
'allowSettingSiteDefault',
|
|
||||||
'compact',
|
|
||||||
],
|
|
||||||
computed: {
|
|
||||||
siteDefaultCrop() {
|
|
||||||
if (!this.siteSettings) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return JSON.stringify(
|
|
||||||
this.siteSettings.data.defaults.crop
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.isEditing) {
|
|
||||||
this.enableEditMode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
isEditing(newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.enableEditMode();
|
|
||||||
} else {
|
|
||||||
this.disableEditMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Sets default crop, for either site or global
|
|
||||||
*/
|
|
||||||
setDefaultCrop($event, scope) {
|
|
||||||
if (!this.siteSettings) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 || !this.siteSettings) {
|
|
||||||
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="@csui/src/res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
@ -1,254 +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),
|
|
||||||
'b3-compact': compact,
|
|
||||||
b3: !compact
|
|
||||||
}"
|
|
||||||
: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>
|
|
||||||
<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>
|
|
||||||
</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>
|
|
||||||
<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 class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.stretch.selected.label">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</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>
|
|
||||||
<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 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 v-if="siteSettings && allowSettingSiteDefault" 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',
|
|
||||||
'allowSettingSiteDefault',
|
|
||||||
'compact',
|
|
||||||
],
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
siteDefaultStretch() {
|
|
||||||
if (!this.siteSettings) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return JSON.stringify(
|
|
||||||
this.siteSettings.data.defaults.stretch
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.isEditing) {
|
|
||||||
this.enableEditMode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
isEditing(newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.enableEditMode();
|
|
||||||
} else {
|
|
||||||
this.disableEditMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Sets default stretching mode, for either site or global
|
|
||||||
*/
|
|
||||||
setDefaultStretchingMode($event, globalOrSite) {
|
|
||||||
if (!this.siteSettings) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 || !this.siteSettings) {
|
|
||||||
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="@csui/res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
@ -1,313 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
|
|
||||||
<div class="sub-panel-content flex flex-row flex-wrap">
|
|
||||||
|
|
||||||
<ShortcutButton
|
|
||||||
v-for="(command, index) of settings?.active.commands.zoom"
|
|
||||||
class="flex button"
|
|
||||||
:class="{active: editMode ? index === editModeOptions?.zoom?.selectedIndex : isActiveZoom(command),
|
|
||||||
'b3-compact': compact,
|
|
||||||
b3: !compact
|
|
||||||
}"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="editMode ? editAction(command, index, 'zoom') : 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-zoom', label: 'New aspect ratio', arguments: {type: AspectRatioType.Fixed}},
|
|
||||||
null,
|
|
||||||
'zoom'
|
|
||||||
)"
|
|
||||||
></ShortcutButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-if="isEditing">
|
|
||||||
<div v-if="editMode && editModeOptions?.zoom?.selected" class="sub-panel-content">
|
|
||||||
<div class="edit-action-area-header">
|
|
||||||
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.zoom?.selected?.label}}</b>
|
|
||||||
<template v-if="editModeOptions?.zoom?.selectedIndex === null && editModeOptions?.zoom?.selected?.label !== 'New aspect ratio'">(New ratio)</template>
|
|
||||||
</div>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<!-- Some options are only shown for type 4 (fixed) zooms -->
|
|
||||||
<template v-if="editModeOptions?.zoom?.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.zoom.selected.arguments.ratio"
|
|
||||||
@blur="editModeOptions.zoom.selected.label === 'New aspect ratio' ? editModeOptions.zoom.selected.label = editModeOptions.zoom.selected.arguments.ratio : null"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</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 class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.zoom.selected.label">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- editing keyboard shortcuts is always allowed -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Shortcut:</div>
|
|
||||||
<div class="">
|
|
||||||
<EditShortcutButton
|
|
||||||
:shortcut="editModeOptions?.zoom?.selected?.shortcut"
|
|
||||||
@shortcutChanged="updateSelectedShortcut($event, 'zoom')"
|
|
||||||
>
|
|
||||||
</EditShortcutButton>
|
|
||||||
</div>
|
|
||||||
</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 class="flex flex-row flex-end">
|
|
||||||
<div
|
|
||||||
v-if="editModeOptions?.zoom?.selected?.arguments?.type === AspectRatioType.Fixed && editModeOptions?.zoom?.selectedIndex !== null"
|
|
||||||
class="button"
|
|
||||||
@click="deleteAction('zoom')"
|
|
||||||
>
|
|
||||||
<mdicon name="delete"></mdicon> Delete
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div class="button" @click="cancelEdit('zoom')">Cancel</div>
|
|
||||||
<div class="button" @click="saveShortcut('zoom')">
|
|
||||||
<mdicon name="floppy"></mdicon>
|
|
||||||
|
|
||||||
<template v-if="editModeOptions?.zoom?.selectedIndex === null">Add</template>
|
|
||||||
<template v-else>Save</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<!--
|
|
||||||
min, max and value need to be implemented in js as this slider
|
|
||||||
should use logarithmic scale
|
|
||||||
-->
|
|
||||||
<div class="flex flex-row w-full" style="margin-top: 0.66rem">
|
|
||||||
<div style="position:relative;" class="grow">
|
|
||||||
<template v-if="zoomAspectRatioLocked">
|
|
||||||
<div class="slider-label">
|
|
||||||
Zoom: {{getZoomForDisplay('x')}}
|
|
||||||
</div>
|
|
||||||
<input id="_input_zoom_slider"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="slider-label">Horizontal zoom: {{getZoomForDisplay('x')}}</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 class="slider-label">Vertical zoom: {{getZoomForDisplay('y')}}</div>
|
|
||||||
<input id="_input_zoom_slider_2"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
:value="zoom.y"
|
|
||||||
@input="changeZoom($event.target.value, 'y')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row items-center justify-center" style="padding-left: 1rem">
|
|
||||||
<Button
|
|
||||||
v-if="zoomAspectRatioLocked"
|
|
||||||
icon="lock"
|
|
||||||
:iconSize="16"
|
|
||||||
:fixedWidth="true"
|
|
||||||
:noPad="true"
|
|
||||||
@click="toggleZoomAr()"
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
v-else
|
|
||||||
icon="lock-open-variant"
|
|
||||||
:iconSize="16"
|
|
||||||
:fixedWidth="true"
|
|
||||||
:noPad="true"
|
|
||||||
@click="toggleZoomAr()"
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
icon="restore"
|
|
||||||
:iconSize="16"
|
|
||||||
:noPad="true"
|
|
||||||
@click="resetZoom()"
|
|
||||||
></Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Button from '@csui/src/components/Button.vue';
|
|
||||||
import ShortcutButton from '@csui/src/components/ShortcutButton.vue';
|
|
||||||
import EditShortcutButton from '@csui/src/components/EditShortcutButton';
|
|
||||||
import EditModeMixin from '@csui/src/utils/EditModeMixin';
|
|
||||||
import KeyboardShortcutParserMixin from '@csui/src/utils/KeyboardShortcutParserMixin';
|
|
||||||
import CommsMixin from '@csui/src/utils/CommsMixin';
|
|
||||||
import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Button,
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
// ComputeActionsMixin,
|
|
||||||
EditModeMixin,
|
|
||||||
KeyboardShortcutParserMixin,
|
|
||||||
CommsMixin
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
'isEditing',
|
|
||||||
'compact',
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
AspectRatioType,
|
|
||||||
|
|
||||||
zoomAspectRatioLocked: true,
|
|
||||||
zoom: {
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: this should be mixin?
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.isEditing) {
|
|
||||||
this.enableEditMode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
isEditing(newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.enableEditMode();
|
|
||||||
} else {
|
|
||||||
this.disableEditMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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});
|
|
||||||
},
|
|
||||||
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});
|
|
||||||
} else {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: {[axis ?? 'x']: newZoom}});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isActiveZoom(command) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.input-slider {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing:border-box;
|
|
||||||
margin-right: 1rem;
|
|
||||||
margin-left: 0rem;
|
|
||||||
}
|
|
||||||
.slider-label {
|
|
||||||
margin-bottom: -0.5rem;
|
|
||||||
color: #aaa;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,691 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h1>Video player options</h1>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div style="width: 48%">
|
|
||||||
NEW PLAYER SELECTOR
|
|
||||||
<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 be highlighted with golden outline. Player should be the first element that covers the video player on the page.
|
|
||||||
</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>
|
|
||||||
<div style="width: 48%">
|
|
||||||
<div>EXAMPLE <small style="opacity: 0.5"><br/>ps: i know this ui is shit, no need to tell me</small></div>
|
|
||||||
<div class="demo-images">
|
|
||||||
<div class="fig1">
|
|
||||||
<img src="/res/img/player-select-demo/uw_player_select___too_little.webp" />
|
|
||||||
<div>If you see this when hovering over the element, you need to select further down the list.</div>
|
|
||||||
</div>
|
|
||||||
<div class="fig2">
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
If you see this when hovering over the element, you're hovering over the correct element.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
In case there's more than one element that covers the entire player and nothing more, select the option that's closest to the top of the list, otherwise in-player UI could break.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
If in-player UI breaks, you can make the settings window appear from the extension popup.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<img src="/res/img/player-select-demo/uw_player_select___just-right.webp" />
|
|
||||||
</div>
|
|
||||||
<div class="fig1">
|
|
||||||
<img src="/res/img/player-select-demo/uw_player_select___too_much.webp" />
|
|
||||||
<div>If you see this when hovering over the element, you need to select an element closer to the top of the list.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="width: 48%" v-if="false">
|
|
||||||
<h2>Legacy advanced settings</h2>
|
|
||||||
|
|
||||||
<div class="">
|
|
||||||
<h3>Player element</h3>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Manually specify player</div>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Select player by/as:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select>
|
|
||||||
<option>n-th parent of video</option>
|
|
||||||
<option>Query selectors</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Player is n-th parent of video</div>
|
|
||||||
<div class="range-input">
|
|
||||||
<input type="range" min="0" max="100" />
|
|
||||||
<input />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Query selector for player element</div>
|
|
||||||
<div class="input">
|
|
||||||
<input />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">In full screen, calculate crop based on monitor resolution</div>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>Video element</h3>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Select video element automatically</div>
|
|
||||||
<input type="checkbox">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Query selectors</div>
|
|
||||||
<div class="input">
|
|
||||||
<input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Additional styles for video element</div>
|
|
||||||
<div class="input">
|
|
||||||
<textarea></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>Additional css for this page</h3>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Additional CSS for this page</div>
|
|
||||||
<textarea></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="sub-panel-content">
|
|
||||||
<h2>Advanced settings</h2>
|
|
||||||
</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',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config) => this.handleElementStack(config)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getPlayerTree();
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribeAll(this);
|
|
||||||
},
|
|
||||||
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" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped module></style>
|
|
||||||
<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%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.demo-images {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
padding-top: 2rem;
|
|
||||||
|
|
||||||
.fig1, .fig2 {
|
|
||||||
margin-top: -2rem;
|
|
||||||
max-height: 18rem;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.fig1 {
|
|
||||||
align-self: start;
|
|
||||||
}
|
|
||||||
.fig2 {
|
|
||||||
align-self: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 32rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,296 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col" style="position: relative; width: 100%;">
|
|
||||||
<!-- The rest of the tab is under 'edit ratios and shortcuts' row -->
|
|
||||||
<div class="flex flex-col" style="width: 100%">
|
|
||||||
<h2>Player UI options</h2>
|
|
||||||
|
|
||||||
<div class="flex flex-col compact-form">
|
|
||||||
<div v-if="!siteSettings.data.enableUI.fullscreen">
|
|
||||||
UI is disabled for this site.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="flex flex-col field-group compact-form"
|
|
||||||
:class="{disabled: !siteSettings.data.enableUI.fullscreen}"
|
|
||||||
>
|
|
||||||
<div class="field disabled">
|
|
||||||
<div class="label">
|
|
||||||
Popup activator position:
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="settings.active.ui.inPlayer.popupAlignment"
|
|
||||||
@change="saveSettings()"
|
|
||||||
>
|
|
||||||
<option value="left">Left</option>
|
|
||||||
<option value="right">Right</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Activate in-player UI:
|
|
||||||
</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="settings.active.ui.inPlayer.activation"
|
|
||||||
@change="saveSettings()"
|
|
||||||
>
|
|
||||||
<option value="player">
|
|
||||||
When mouse hovers over player
|
|
||||||
</option>
|
|
||||||
<option value="trigger-zone">
|
|
||||||
When mouse hovers over trigger zone
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field" :class="{'disabled': settings.active.ui.inPlayer.activation !== 'trigger-zone'}">
|
|
||||||
<div class="label">Edit trigger zone:</div>
|
|
||||||
<button @click="startTriggerZoneEdit()">Edit</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Do not show in-player UI when video player is narrower than
|
|
||||||
</div>
|
|
||||||
<div class="input range-input">
|
|
||||||
<input
|
|
||||||
:value="settings.active.ui.inPlayer.minEnabledWidth"
|
|
||||||
class="slider"
|
|
||||||
type="range"
|
|
||||||
min="0"
|
|
||||||
max="1"
|
|
||||||
step="0.01"
|
|
||||||
@input="(event) => setPlayerRestrictions('minEnabledWidth', event.target.value)"
|
|
||||||
@change="(event) => saveSettings()"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
style="margin-right: 0.6rem;"
|
|
||||||
:value="ghettoComputed.minEnabledWidth"
|
|
||||||
@input="(event) => setPlayerRestrictions('minEnabledWidth', event.target.value, true)"
|
|
||||||
@change="(event) => saveSettings(true)"
|
|
||||||
>
|
|
||||||
<div class="unit">% of screen</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Do not show in-player UI when video player is shorter than
|
|
||||||
</div>
|
|
||||||
<div class="input range-input">
|
|
||||||
<input
|
|
||||||
:value="settings.active.ui.inPlayer.minEnabledHeight"
|
|
||||||
class="slider"
|
|
||||||
type="range"
|
|
||||||
min="0"
|
|
||||||
max="1"
|
|
||||||
step="0.01"
|
|
||||||
@input="(event) => setPlayerRestrictions('minEnabledHeight', event.target.value)"
|
|
||||||
@change="(event) => saveSettings()"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
style="margin-right: 0.6rem;"
|
|
||||||
:value="ghettoComputed.minEnabledHeight"
|
|
||||||
@input="(event) => setPlayerRestrictions('minEnabledHeight', event.target.value, true)"
|
|
||||||
@change="(event) => saveSettings(true)"
|
|
||||||
>
|
|
||||||
<div class="unit">% of screen</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="mt2r">Menu options and keyboard shortcuts</h2>
|
|
||||||
<div>
|
|
||||||
Click 'add new' to add a new option. Click a button to edit or remove the keyboard shortcut.
|
|
||||||
</div>
|
|
||||||
<div class="keyboard-settings">
|
|
||||||
<!-- CROP OPTIONS -->
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h3 class="mth3">CROP OPTIONS</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CropOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="true"
|
|
||||||
>
|
|
||||||
</CropOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STRETCH OPTIONS -->
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h3 class="mth3">STRETCH OPTIONS</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StretchOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="true"
|
|
||||||
></StretchOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ZOOM OPTIONS -->
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h3 class="mth3">ZOOM OPTIONS</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ZoomOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="true"
|
|
||||||
></ZoomOptionsPanel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Button from '@csui/src/components/Button.vue'
|
|
||||||
import BrowserDetect from '@src/ext/conf/BrowserDetect';
|
|
||||||
import CropOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/CropOptionsPanel.vue'
|
|
||||||
import StretchOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/StretchOptionsPanel.vue'
|
|
||||||
import ZoomOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/ZoomOptionsPanel.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Button,
|
|
||||||
CropOptionsPanel,
|
|
||||||
StretchOptionsPanel,
|
|
||||||
ZoomOptionsPanel,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
ghettoComputed: { }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
],
|
|
||||||
mounted() {
|
|
||||||
this.ghettoComputed = {
|
|
||||||
minEnabledWidth: this.optionalToFixed(this.settings.active.ui.inPlayer.minEnabledWidth * 100, 0),
|
|
||||||
minEnabledHeight: this.optionalToFixed(this.settings.active.ui.inPlayer.minEnabledHeight * 100, 0),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
forcePositiveNumber(value) {
|
|
||||||
// Change EU format to US if needed
|
|
||||||
// | remove everything after second period if necessary
|
|
||||||
// | | | remove non-numeric characters
|
|
||||||
// | | | |
|
|
||||||
return value.replaceAll(',', '.').split('.', 2).join('.').replace(/[^0-9.]/g, '');
|
|
||||||
},
|
|
||||||
optionalToFixed(v, n) {
|
|
||||||
if ((`${v}`.split('.')[1]?.length ?? 0) > n) {
|
|
||||||
return v.toFixed(n);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
},
|
|
||||||
setPlayerRestrictions(key, value, isTextInput) {
|
|
||||||
if (isTextInput) {
|
|
||||||
value = (+this.forcePositiveNumber(value) / 100);
|
|
||||||
}
|
|
||||||
if (isNaN(+value)) {
|
|
||||||
value = 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings.active.ui.inPlayer[key] = value;
|
|
||||||
|
|
||||||
if (isTextInput) {
|
|
||||||
this.ghettoComputed[key] = this.optionalToFixed(value, 0);
|
|
||||||
} else {
|
|
||||||
this.ghettoComputed[key] = this.optionalToFixed(value * 100, 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveSettings(forceRefresh) {
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
|
|
||||||
if (forceRefresh) {
|
|
||||||
this.$nextTick( () => this.$forceRefresh() );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
startTriggerZoneEdit() {
|
|
||||||
this.eventBus.send('start-trigger-zone-edit');
|
|
||||||
},
|
|
||||||
async openOptionsPage() {
|
|
||||||
BrowserDetect.runtime.openOptionsPage();
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
max-width: 24rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger-zone-editor {
|
|
||||||
background-color: rgba(0,0,0,0.25);
|
|
||||||
|
|
||||||
padding-bottom: 2rem;
|
|
||||||
.field {
|
|
||||||
margin-bottom: -1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
/* color: #666; */
|
|
||||||
filter: contrast(50%) brightness(40%) grayscale(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-form {
|
|
||||||
> .field, > .field-group {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.keyboard-settings {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 1rem;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
width: calc(33% - 0.5rem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt2r {
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
.mth3 {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,30 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<h1>Reset and backup</h1>
|
|
||||||
<p>
|
|
||||||
Pressing the button will reset settings to default without asking.
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
class="danger"
|
|
||||||
@click="resetSettings"
|
|
||||||
>
|
|
||||||
Reset settings
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
settings: Object
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
resetSettings() {
|
|
||||||
this.settings.active = JSON.parse(JSON.stringify(this.settings.default));
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped module></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>
|
|