cleanup
This commit is contained in:
parent
74c0358d33
commit
3457c4fcdc
@ -5,7 +5,7 @@ const fs = require('fs');
|
|||||||
|
|
||||||
const BUNDLE_DIR = path.join(__dirname, `../dist-${process.env.BROWSER === 'firefox' ? 'ff' : process.env.BROWSER}`);
|
const BUNDLE_DIR = path.join(__dirname, `../dist-${process.env.BROWSER === 'firefox' ? 'ff' : process.env.BROWSER}`);
|
||||||
const bundles = [
|
const bundles = [
|
||||||
'csui/csui-popup.js',
|
// 'csui/csui-popup.js',
|
||||||
];
|
];
|
||||||
|
|
||||||
const evalRegexForProduction = /;([a-z])=function\(\){return this}\(\);try{\1=\1\|\|Function\("return this"\)\(\)\|\|\(0,eval\)\("this"\)}catch\(t\){"object"==typeof window&&\(\1=window\)}/g;
|
const evalRegexForProduction = /;([a-z])=function\(\){return this}\(\);try{\1=\1\|\|Function\("return this"\)\(\)\|\|\(0,eval\)\("this"\)}catch\(t\){"object"==typeof window&&\(\1=window\)}/g;
|
||||||
|
|||||||
@ -1,586 +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/Settings';
|
|
||||||
import EventBus from '../ext/lib/EventBus';
|
|
||||||
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) => {
|
|
||||||
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({site: 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/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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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>
|
|
||||||
@ -11,6 +11,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="uw-ultrawidify-container-root">
|
<body class="uw-ultrawidify-container-root">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script src="csui-global.js"></script>
|
<!-- <script src="csui-global.js"></script> -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -8,6 +8,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="uw-ultrawidify-container-root" style="background-color: transparent;">
|
<body class="uw-ultrawidify-container-root" style="background-color: transparent;">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script src="csui.js"></script>
|
<!-- <script src="csui.js"></script> -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -8,6 +8,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="uw-ultrawidify-container-root" style="background-color: transparent;">
|
<body class="uw-ultrawidify-container-root" style="background-color: transparent;">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script src="csui.js"></script>
|
<!-- <script src="csui.js"></script> -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -7,6 +7,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="uw-ultrawidify-container-root" style="background-color: transparent;">
|
<body class="uw-ultrawidify-container-root" style="background-color: transparent;">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script src="csui.js"></script>
|
<!-- <script src="csui.js"></script> -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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,103 +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.4.0</h2>
|
|
||||||
<ul>
|
|
||||||
<li>In-player UI can now appear in full-screen for websites that put players into top layer</li>
|
|
||||||
<li>Embedded sites now inherit settings of the parent frame. <small>However, this hasn't been tested for all edge cases and may contain bugs.</small></li>
|
|
||||||
<li>Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.</li>
|
|
||||||
<li>Autodetection can be set to stop after first aspect ratio detection, or after a period of no changes.</li>
|
|
||||||
<li>
|
|
||||||
There is a new, experiental mode for autodetection. At this point, it can be manually enabled in the autodetection settings. It will become the default option in 2026.<br/>
|
|
||||||
If you enable experimental mode, please consider reporting problems <a href="https://github.com/tamius-han/ultrawidify/issues/291" target="_blank">in this thread</a> on Github.
|
|
||||||
</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,253 +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="updateLabel('crop')"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editModeOptions.crop?.error" class="hint error">
|
|
||||||
{{editModeOptions.crop.error}}
|
|
||||||
</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,257 +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="updateLabel('stretch')"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editModeOptions.stretch?.error" class="hint error">
|
|
||||||
{{editModeOptions.stretch.error}}
|
|
||||||
</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,317 +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="updateLabel('zoom')"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editModeOptions.zoom?.error" class="hint error">
|
|
||||||
{{editModeOptions.zoom.error}}
|
|
||||||
</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 component
|
|
||||||
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,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,159 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col" style="position: relative; width: 100%;">
|
|
||||||
<!-- 'Change UI' options is a tiny bit in upper right corner. -->
|
|
||||||
<div
|
|
||||||
class="options-bar flex flex-row"
|
|
||||||
:class="{isEditing: editMode}"
|
|
||||||
>
|
|
||||||
<template v-if="editMode">
|
|
||||||
<div style="height: 100%; display: flex; flex-direction: column; justify-content: center; flex: 0 0; padding-right: 8px;">
|
|
||||||
<mdicon name="alert" size="32" />
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow">
|
|
||||||
You are currently editing options and shortcuts.<br/>
|
|
||||||
<b>NOTE: changes will take effect after page reload.</b>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex-nogrow flex-noshrink"
|
|
||||||
@click="editMode = !editMode"
|
|
||||||
>
|
|
||||||
Exit edit mode
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div
|
|
||||||
class=""
|
|
||||||
@click="editMode = !editMode"
|
|
||||||
>
|
|
||||||
Edit ratios and shortcuts
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- The rest of the tab is under 'edit ratios and shortcuts' row -->
|
|
||||||
<div class="flex flex-row flex-wrap" style="width: 100%">
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<!-- CROP OPTIONS -->
|
|
||||||
<div v-if="settings" class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="16" />
|
|
||||||
<h1>Crop video:</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CropOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="editMode"
|
|
||||||
>
|
|
||||||
</CropOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STRETCH OPTIONS -->
|
|
||||||
<div v-if="settings" class="sub-panel">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="stretch-to-page-outline" :size="32" />
|
|
||||||
<h1>Stretch video:</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StretchOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:isEditing="editMode"
|
|
||||||
></StretchOptionsPanel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ZoomOptionsPanel from './PanelComponents/VideoSettings/ZoomOptionsPanel.vue'
|
|
||||||
import CropOptionsPanel from './PanelComponents/VideoSettings/CropOptionsPanel'
|
|
||||||
import StretchOptionsPanel from './PanelComponents/VideoSettings/StretchOptionsPanel'
|
|
||||||
import Button from '../components/Button.vue'
|
|
||||||
import ShortcutButton from '../components/ShortcutButton';
|
|
||||||
import EditShortcutButton from '../components/EditShortcutButton';
|
|
||||||
import ComputeActionsMixin from '../mixins/ComputeActionsMixin';
|
|
||||||
import BrowserDetect from '../../../ext/conf/BrowserDetect';
|
|
||||||
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
|
|
||||||
import CommsMixin from '../utils/CommsMixin';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
exec: null,
|
|
||||||
scope: 'page',
|
|
||||||
|
|
||||||
editMode: false,
|
|
||||||
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
ComputeActionsMixin,
|
|
||||||
CommsMixin,
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'siteSettings',
|
|
||||||
'frame',
|
|
||||||
'eventBus',
|
|
||||||
'site'
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'uw-config-broadcast',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config) => this.handleConfigBroadcast(config)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.eventBus.sendToTunnel('get-ar');
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribeAll(this);
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
ShortcutButton,
|
|
||||||
EditShortcutButton,
|
|
||||||
Button,
|
|
||||||
AlignmentOptionsControlComponent,
|
|
||||||
StretchOptionsPanel,
|
|
||||||
CropOptionsPanel, ZoomOptionsPanel
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
async openOptionsPage() {
|
|
||||||
BrowserDetect.runtime.openOptionsPage();
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="../../res/css/flex.scss" scoped module></style>
|
|
||||||
<style lang="scss" src="@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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,247 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="action">
|
|
||||||
<div class="flex-row action-name-cmd-container">
|
|
||||||
<div class="">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="flex action-name">
|
|
||||||
<span v-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.userAdded || (action.cmd[0].arg === AspectRatioType.Fixed)" class="icon red" @click="removeAction()">🗙</span>
|
|
||||||
<span v-else class="icon transparent">🗙</span>
|
|
||||||
<span class="icon" @click="editAction()">🖉</span>
|
|
||||||
{{action.name}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="command-details">
|
|
||||||
<div class="flex flex-col cmd-container cd-pad">
|
|
||||||
<div class="flex bold">
|
|
||||||
Command:
|
|
||||||
</div>
|
|
||||||
<div class="flex cmdlist">
|
|
||||||
{{parseCommand(action.cmd)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- SCOPES -->
|
|
||||||
<div class="flex flex-col scopes-container cd-pad">
|
|
||||||
<div class="flex bold">Popup tabs:</div>
|
|
||||||
|
|
||||||
<!-- global scope -->
|
|
||||||
<div v-if="action.scopes.global"
|
|
||||||
class="flex flex-row scope-row"
|
|
||||||
>
|
|
||||||
<div class="flex scope-scope">
|
|
||||||
Global:
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label scope-visible">
|
|
||||||
Visible? <span class="scope-row-highlight">{{action.scopes.global.show ? 'Yes' : 'No'}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.scopes.global.show"
|
|
||||||
class="flex scope-row-label scope-button-label"
|
|
||||||
>
|
|
||||||
Button label?
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.global.label ? action.scopes.global.label : (action.label ? action.label : '')}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label">
|
|
||||||
Keyboard shortcut:
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.global.shortcut ? parseActionShortcut(action.scopes.global.shortcut[0]) : 'None'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- site scope -->
|
|
||||||
<div v-if="action.scopes.site"
|
|
||||||
class="flex flex-row scope-row"
|
|
||||||
>
|
|
||||||
<div class="flex scope-scope">
|
|
||||||
Site:
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label scope-visible">
|
|
||||||
Visible? <span class="scope-row-highlight">{{action.scopes.site.show ? 'Yes' : 'No'}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.scopes.site.show"
|
|
||||||
class="flex scope-row-label scope-button-label"
|
|
||||||
>
|
|
||||||
Button label?
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.site.label ? action.scopes.site.label : (action.label ? action.label : '')}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label">
|
|
||||||
Keyboard shortcut:
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.site.shortcut ? parseActionShortcut(action.scopes.site.shortcut[0]) : 'None'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- page scope -->
|
|
||||||
<div v-if="action.scopes.page"
|
|
||||||
class="flex flex-row scope-row"
|
|
||||||
>
|
|
||||||
<div class="flex scope-scope">
|
|
||||||
Page:
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label scope-visible">
|
|
||||||
Visible? <span class="scope-row-highlight">{{action.scopes.page.show ? 'Yes' : 'No'}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.scopes.page.show"
|
|
||||||
class="flex scope-row-label scope-button-label"
|
|
||||||
>
|
|
||||||
Button label?
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.page.label ? action.scopes.page.label : (action.label ? action.label : '')}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex scope-row-label">
|
|
||||||
Keyboard shortcut:
|
|
||||||
<span class="scope-row-highlight">
|
|
||||||
{{action.scopes.page.shortcut ? parseActionShortcut(action.scopes.page.shortcut[0]) : 'None'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import StretchType from '../enums/StretchType.enum';
|
|
||||||
import AspectRatioType from '../enums/AspectRatioType.enum';
|
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
StretchType: StretchType,
|
|
||||||
AspectRatioType: AspectRatioType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
action: Object,
|
|
||||||
index: Number,
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
parseActionShortcut(shortcut) {
|
|
||||||
return KeyboardShortcutParser.parseShortcut(shortcut);
|
|
||||||
},
|
|
||||||
parseCommand(cmd) {
|
|
||||||
let cmdstring = '';
|
|
||||||
for (const c of cmd) {
|
|
||||||
cmdstring += `${c.action} ${c.arg}${c.persistent ? ' persistent' : ''}; `;
|
|
||||||
}
|
|
||||||
return cmdstring;
|
|
||||||
},
|
|
||||||
editAction() {
|
|
||||||
this.$emit('edit');
|
|
||||||
},
|
|
||||||
removeAction() {
|
|
||||||
this.$emit('remove');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../res/css/colors.scss';
|
|
||||||
|
|
||||||
.action {
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action .command-details {
|
|
||||||
height: 0px;
|
|
||||||
max-height: 0px;
|
|
||||||
transition: max-height 0.5s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: height 0.5s ease;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action:hover .command-details {
|
|
||||||
height: auto;
|
|
||||||
max-height: 200px;
|
|
||||||
transition: max-height 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-details {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-name-cmd-container, .p1rem {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cd-pad {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-name {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 300;
|
|
||||||
color: $text-normal;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-name:hover, .action:hover .action-name {
|
|
||||||
color: lighten($primary-color, 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
|
||||||
color: $primary-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-container {
|
|
||||||
width: 13.37rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmdlist {
|
|
||||||
font-family: 'Overpass Mono';
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: $text-dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-scope {
|
|
||||||
width: 5rem;
|
|
||||||
text-align: right !important;
|
|
||||||
color: $secondary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-visible {
|
|
||||||
width: 7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-button-label {
|
|
||||||
width: 16rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-row-label {
|
|
||||||
color: $text-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scope-row-highlight {
|
|
||||||
color: $text-normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transparent {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
<template>
|
|
||||||
<tr>
|
|
||||||
<td class="cmd monospace">{{parseCommand(action.cmd)}}</td>
|
|
||||||
<td class="">{{action.label ? action.label : ""}}</td>
|
|
||||||
<td class="shortcut center-text">{{parseActionShortcut(action)}}</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': (action.shortcut && action.shortcut.length &&
|
|
||||||
(action.shortcut[0].onMouseMove || action.shortcut[0].onClick ||
|
|
||||||
action.shortcut[0].onScroll))}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.popup_global}"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.popup_site}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.popup}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="center-text">
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<span class="checkbox"
|
|
||||||
:class="{'checkbox-checked': action.ui}"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>{{action.ui_path ? action.ui_path : ""}}</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import StretchType from '../enums/StretchType.enum';
|
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
StretchType: StretchType
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
action: Object
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
parseActionShortcut(action) {
|
|
||||||
return KeyboardShortcutParser.parseShortcut(action.shortcut[0]);
|
|
||||||
},
|
|
||||||
parseCommand(cmd) {
|
|
||||||
let cmdstring = '';
|
|
||||||
for(const c of cmd) {
|
|
||||||
cmdstring += `${c.action} ${c.arg}${c.persistent ? ' persistent' : ''}; `;
|
|
||||||
}
|
|
||||||
return cmdstring;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="button center-text"
|
|
||||||
:class="{
|
|
||||||
'setting-selected': selected,
|
|
||||||
'no-pad': noPad,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<mdicon v-if="icon" :name="icon" :size="iconSize ?? 24"></mdicon>
|
|
||||||
<div class="label">
|
|
||||||
{{label}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
fixedWidth: Boolean,
|
|
||||||
selected: Boolean,
|
|
||||||
label: String,
|
|
||||||
icon: String,
|
|
||||||
iconSize: Number,
|
|
||||||
noPad: Boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.button {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: none;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
text-align: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.no-pad {
|
|
||||||
padding: 0.5rem 1rem !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-if="popupVisible" class="popup">
|
|
||||||
<div class="popup-window">
|
|
||||||
<div
|
|
||||||
class="header"
|
|
||||||
:class="{
|
|
||||||
'danger': dialogType === 'danger',
|
|
||||||
'warning': dialogType === 'warning',
|
|
||||||
'info': dialogType === 'info',
|
|
||||||
'normal': dialogType === 'normal'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ dialogText || 'Confirm action' }}
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
{{ dialogText || 'Are you sure you want to do that?' }}
|
|
||||||
</div>
|
|
||||||
<div class="footer">
|
|
||||||
<button
|
|
||||||
class="button confirm"
|
|
||||||
:class="{
|
|
||||||
'danger': dialogType === 'danger',
|
|
||||||
'warning': dialogType === 'warning',
|
|
||||||
'info': dialogType === 'info',
|
|
||||||
'normal': dialogType === 'normal'
|
|
||||||
}"
|
|
||||||
@click="confirmAction"
|
|
||||||
>
|
|
||||||
{{ confirmText || 'Confirm' }}
|
|
||||||
</button>
|
|
||||||
<button class="button" @click="popupVisible = false">{{ cancelText || 'Cancel' }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
:class="[
|
|
||||||
{
|
|
||||||
'danger': dialogType === 'danger',
|
|
||||||
'warning': dialogType === 'warning',
|
|
||||||
},
|
|
||||||
btnClass
|
|
||||||
]"
|
|
||||||
@click="popupVisible = true"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
popupVisible: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: [
|
|
||||||
'btnClass',
|
|
||||||
'dialogTitle',
|
|
||||||
'dialogText',
|
|
||||||
'confirmText',
|
|
||||||
'cancelText',
|
|
||||||
'dialogType'
|
|
||||||
],
|
|
||||||
methods: {
|
|
||||||
confirmAction() {
|
|
||||||
this.popupVisible = false;
|
|
||||||
this.$emit('onConfirmed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.popup {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
backdrop-filter: saturate(50%) brightness(50%) blur(1rem);
|
|
||||||
z-index: 99999;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
font-size: 1.33rem;
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
color: rgb(251, 107, 63);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.body {
|
|
||||||
min-height: 5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,190 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div
|
|
||||||
class="flex-grow button"
|
|
||||||
@click="editShortcut()"
|
|
||||||
|
|
||||||
>
|
|
||||||
<template v-if="!editing">
|
|
||||||
{{shortcutDisplay}}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{currentKeypress ?? 'Press a key'}}
|
|
||||||
<input ref="input"
|
|
||||||
class="hidden-input"
|
|
||||||
@keyup.capture="keyup($event)"
|
|
||||||
@keydown.capture="keydown($event)"
|
|
||||||
@input.prevent=""
|
|
||||||
@blur="editing = false;"
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class="button" @click="$emit('shortcutChanged', null)">
|
|
||||||
<mdicon name="delete"></mdicon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import KeyboardShortcutParser from '../../../common/js/KeyboardShortcutParser';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
shortcut: Object,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
currentKeypress: undefined,
|
|
||||||
currentKey: undefined,
|
|
||||||
editing: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
shortcutDisplay() {
|
|
||||||
if (!this.shortcut) {
|
|
||||||
return '(no shortcut)'
|
|
||||||
}
|
|
||||||
return KeyboardShortcutParser.parseShortcut(this.shortcut);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
editShortcut() {
|
|
||||||
this.editing = true;
|
|
||||||
this.currentKeypress = undefined;
|
|
||||||
this.currentKey = undefined;
|
|
||||||
|
|
||||||
// input doesn't exist now, but will exist on the next tick
|
|
||||||
this.$nextTick(()=> this.$refs.input.focus());
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates currently pressed keypress for display
|
|
||||||
*/
|
|
||||||
keydown(event) {
|
|
||||||
// event.repeat is set to 'true' when key is being held down, but not on
|
|
||||||
// first keydown. We don't need to process subsequent repeats of a keypress
|
|
||||||
// we already processed.
|
|
||||||
if (event.repeat) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const shortcut = KeyboardShortcutParser.generateShortcutFromKeypress(event);
|
|
||||||
const fixedShortcut = this.handleModifierKeypress(shortcut);
|
|
||||||
|
|
||||||
if (this.currentKey === undefined) {
|
|
||||||
this.currentKey = fixedShortcut;
|
|
||||||
} else {
|
|
||||||
// here's a fun fact. Keydown doesn't do modifier keys the way we want —
|
|
||||||
// notably, A-Z0-9 keys are returned without modifier state (all modifiers)
|
|
||||||
// are set to false in keydown events. That means we need to keep track of
|
|
||||||
// modifiers ourselves.
|
|
||||||
if (fixedShortcut.notModifier) {
|
|
||||||
this.currentKey.key = fixedShortcut.key;
|
|
||||||
this.currentKey.code = fixedShortcut.code;
|
|
||||||
} else {
|
|
||||||
this.currentKey = {
|
|
||||||
...fixedShortcut,
|
|
||||||
key: this.currentKey.key,
|
|
||||||
code: this.currentKey.code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update display
|
|
||||||
this.currentKeypress = KeyboardShortcutParser.parseShortcut(this.currentKey);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Emits shortcutChanged when shortcut is considered changed
|
|
||||||
*/
|
|
||||||
keyup(event) {
|
|
||||||
const shortcut = KeyboardShortcutParser.generateShortcutFromKeypress(event);
|
|
||||||
const fixedShortcut = this.handleModifierKeypress(shortcut);
|
|
||||||
|
|
||||||
if (fixedShortcut.notModifier) {
|
|
||||||
this.editing = false;
|
|
||||||
this.$emit('shortcutChanged', this.currentKey);
|
|
||||||
} else {
|
|
||||||
// if none of the modifiers are pressed and if no other key is being held down,
|
|
||||||
// we need to reset label back to 'pls press key'
|
|
||||||
if (!fixedShortcut.altKey && !fixedShortcut.ctrlKey && !fixedShortcut.metaKey && !fixedShortcut.shiftKey && !fixedShortcut.code) {
|
|
||||||
this.currentKeypress = undefined;
|
|
||||||
this.currentKey = undefined;
|
|
||||||
} else {
|
|
||||||
this.currentKey = shortcut;
|
|
||||||
this.currentKeypress = KeyboardShortcutParser.parseShortcut(this.currentKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles current keypress if event.keyCode is a modifier key.
|
|
||||||
* Returns true if current event was a modifier key, false if
|
|
||||||
* if was a regular A-Z 0-9 key.
|
|
||||||
*/
|
|
||||||
handleModifierKeypress(event) {
|
|
||||||
const modifierPressed = event.type === 'keydown';
|
|
||||||
|
|
||||||
switch (event.code) {
|
|
||||||
case 'ShiftLeft':
|
|
||||||
case 'ShiftRight':
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
key: '…',
|
|
||||||
code: null,
|
|
||||||
shiftKey: modifierPressed
|
|
||||||
}
|
|
||||||
case 'ControlLeft':
|
|
||||||
case 'ControlRight':
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
key: '…',
|
|
||||||
code: null,
|
|
||||||
controlKey: modifierPressed
|
|
||||||
};
|
|
||||||
case 'MetaLeft':
|
|
||||||
case 'MetaRight':
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
key: '…',
|
|
||||||
code: null,
|
|
||||||
metaKey: modifierPressed
|
|
||||||
};
|
|
||||||
case 'AltLeft':
|
|
||||||
case 'AltRight':
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
key: '…',
|
|
||||||
code: null,
|
|
||||||
altKey: modifierPressed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
notModifier: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
.center-text {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.dark {
|
|
||||||
opacity: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-input {
|
|
||||||
position: absolute;
|
|
||||||
z-index: -9999;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-row w-full">
|
|
||||||
<button
|
|
||||||
v-if="editorMode === 'tree'"
|
|
||||||
@click="() => setEditorMode('text')"
|
|
||||||
>
|
|
||||||
Edit as text
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
@click="() => setEditorMode('tree')"
|
|
||||||
>
|
|
||||||
Show as tree
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div ref="refContainer" class="w-full h-full"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { createJSONEditor, Mode } from "vanilla-jsoneditor";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
modelValue: Object // v-model binding
|
|
||||||
},
|
|
||||||
emits: ["update:modelValue"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
refContainer: null,
|
|
||||||
editor: null,
|
|
||||||
editorMode: Mode.tree,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
modelValue: {
|
|
||||||
deep: true,
|
|
||||||
handler(newValue) {
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.updateProps({ content: { json: newValue || {} } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (this.$refs.refContainer) {
|
|
||||||
this.editor = createJSONEditor({
|
|
||||||
target: this.$refs.refContainer,
|
|
||||||
props: {
|
|
||||||
mode: Mode.tree,
|
|
||||||
content: { json: this.modelValue || {} },
|
|
||||||
onChange: (updatedContent) => {
|
|
||||||
this.$emit("update:modelValue", updatedContent.json);
|
|
||||||
},
|
|
||||||
onRenderContextMenu: false,
|
|
||||||
navigationBar: false,
|
|
||||||
statusBar: false,
|
|
||||||
mainMenuBar: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setEditorMode(mode) {
|
|
||||||
this.editorMode = mode
|
|
||||||
this.editor.updateProps({ mode });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.destroy();
|
|
||||||
this.editor = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
:root {
|
|
||||||
--jse-panel-background: #111;
|
|
||||||
--jse-background-color: #000;
|
|
||||||
--jse-text-color: #ccc;
|
|
||||||
--jse-key-color: #8c8bef;
|
|
||||||
--jse-selection-background-color: rgba(255, 171, 102, 0.177);
|
|
||||||
--jse-context-menu-pointer-background: rgba(255, 171, 102, 0.177);
|
|
||||||
--jse-delimiter-color: #fa6;
|
|
||||||
--jse-value-color-boolean: rgb(132, 132, 137);
|
|
||||||
}
|
|
||||||
|
|
||||||
.jse-boolean-toggle[role="checkbox"] {
|
|
||||||
&[aria-checked="true"] {
|
|
||||||
color: rgb(218, 244, 238);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[aria-checked="false"] {
|
|
||||||
border: 0px transparent;
|
|
||||||
outline: 0px transparent;
|
|
||||||
color: rgb(241, 107, 25);
|
|
||||||
background-color: rgb(241, 107, 25);
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
content: '×';
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="root">
|
|
||||||
<template v-for="key in json"
|
|
||||||
>
|
|
||||||
<div class="element" :key="key">
|
|
||||||
<span class="json-key">"{{key}}"</span>:
|
|
||||||
<template v-if="Array.isArray(json[key])">
|
|
||||||
[<br/>
|
|
||||||
<div v-for="index of json[key]"
|
|
||||||
class="json-table-row"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
]
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "json-explorer",
|
|
||||||
props: {
|
|
||||||
json: Object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
v-if="message"
|
|
||||||
class="popup-overlay"
|
|
||||||
:class="{'dim': dimOverlay}"
|
|
||||||
>
|
|
||||||
<div class="popup-content" >
|
|
||||||
<div v-if="title" class="header" :class="type">
|
|
||||||
{{title}}
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
{{ message }}
|
|
||||||
</p>
|
|
||||||
<div class="flex flex-row justify-end">
|
|
||||||
<button
|
|
||||||
class="primary"
|
|
||||||
v-if="confirmButtonText"
|
|
||||||
:class="type"
|
|
||||||
@click="$emit('onConfirm')"
|
|
||||||
>
|
|
||||||
{{ confirmButtonText }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
:class="type"
|
|
||||||
@click="$emit('onCancel')"
|
|
||||||
>
|
|
||||||
{{ cancelButtonText }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
// onConfirm: {
|
|
||||||
// type: Function,
|
|
||||||
// required: false
|
|
||||||
// },
|
|
||||||
// onCancel: {
|
|
||||||
// type: Function,
|
|
||||||
// default: () => this.$emit('close')
|
|
||||||
// },
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
type: String,
|
|
||||||
required: false, // technically prolly should be true, but we don't have to guard against skill issue
|
|
||||||
},
|
|
||||||
dimOverlay: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'info'
|
|
||||||
},
|
|
||||||
confirmButtonText: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
cancelButtonText: {
|
|
||||||
type: String,
|
|
||||||
default: 'Cancel'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.popup-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
backdrop-filter: saturate(50%) brightness(50%) blur(1rem);
|
|
||||||
z-index: 99999;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
font-size: 1.33rem;
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
color: rgb(251, 107, 63);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.body {
|
|
||||||
min-height: 5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col h100">
|
|
||||||
<div class="row">
|
|
||||||
<span class="label">Ultrawidify version:</span><br/> {{addonVersion}}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span class="label">Having an issue?</span><br/> Report <strike>undocumented features</strike> bugs using one of the following options (in order of preference):
|
|
||||||
<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">{{gmailLink}}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// import Comms from '@src/ext/lib/comms/Comms';
|
|
||||||
// import ShortcutButton from '@src/common/components/ShortcutButton';
|
|
||||||
import BrowserDetect from '@src/ext/conf/BrowserDetect';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// reminder — webextension-polyfill doesn't seem to work in vue!
|
|
||||||
addonVersion: BrowserDetect.firefox ? chrome.runtime.getManifest().version : chrome.runtime.getManifest().version,
|
|
||||||
loggingEnabled: false,
|
|
||||||
loggerSettings: '',
|
|
||||||
loggerSettingsError: false,
|
|
||||||
lastLoadedLoggerSettings: undefined,
|
|
||||||
mailtoLink: '',
|
|
||||||
showEasterEgg: false,
|
|
||||||
gmailLink: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
const b64="dGFtaXVzLmhhbkBnbWFpbC5jb20";
|
|
||||||
this.gmailLink = atob(b64);
|
|
||||||
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:${this.gmailLink}?subject=%5BUltrawidify%5D%20ENTER%20SUMMARY%20OF%20YOUR%20ISSUE%20HERE&body=${messageTemplate}`;
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async updateLoggerSettings(allowLogging) {
|
|
||||||
// this.loggingEnabled = allowLogging;
|
|
||||||
// if (allowLogging) {
|
|
||||||
// const parsedSettings = JSON.parse(this.loggerSettings);
|
|
||||||
// Logger.saveConfig({
|
|
||||||
// allowLogging: allowLogging,
|
|
||||||
// timeout: parsedSettings.timeout || undefined,
|
|
||||||
// fileOptions: parsedSettings.fileOptions || {},
|
|
||||||
// consoleOptions: parsedSettings.consoleOptions || {},
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// // we need to save logger settings because logging is triggered by allow logging
|
|
||||||
// Logger.saveConfig({allowLogging: allowLogging, ...lastLoadedLoggerSettings});
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
// showLogger() {
|
|
||||||
// Comms.sendMessage({cmd: 'show-logger', forwardToActive: true});
|
|
||||||
// },
|
|
||||||
// sendMark() {
|
|
||||||
// this.showEasterEgg = !this.showEasterEgg;
|
|
||||||
// },
|
|
||||||
// hideLogger() {
|
|
||||||
// Comms.sendMessage({cmd: 'hide-logger', forwardToActive: true});
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
@ -1,210 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col relative h-full" style="padding-bottom: 20px">
|
|
||||||
<!--
|
|
||||||
Extension is disabled for a given site when it's disabled in full screen, since
|
|
||||||
current settings do not allow the extension to only be disabled while in full screen
|
|
||||||
-->
|
|
||||||
<template v-if="siteSettings.isEnabledForEnvironment(false, true) === ExtensionMode.Disabled && !enabledHosts?.length">
|
|
||||||
<div class="h-full flex flex-col items-center justify-center" style="margin-top: 8rem">
|
|
||||||
<div class="info">
|
|
||||||
Extension is not enabled for this site.
|
|
||||||
</div>
|
|
||||||
<div class="text-center">
|
|
||||||
<p>Please enable extension for this site.</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="flex flex-row items-center"
|
|
||||||
style="background-color: transparent; padding: 0.5rem 1rem; margin-top: 1rem; border: 1px solid #fa68;"
|
|
||||||
@click="openSettings()"
|
|
||||||
>
|
|
||||||
Open settings <mdicon style="margin-left: 0.5rem;" name="open-in-new" size="16"></mdicon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="text-center">
|
|
||||||
<p><small>Extension may work on content embedded from other sites.</small></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div
|
|
||||||
v-if="siteSettings.isEnabledForEnvironment(false, true) === ExtensionMode.Disabled"
|
|
||||||
class="warning-compact"
|
|
||||||
>
|
|
||||||
<div class="w-full flex flex-row">
|
|
||||||
<div class="grow">
|
|
||||||
<b>Extension is disabled for this site.</b>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="flex flex-row items-center"
|
|
||||||
style="border: 1px solid black; background-color: transparent; color: black; padding: 0.25rem 0.5rem; margin-top: -0.25rem; margin-right: -0.5rem;"
|
|
||||||
@click="openSettings()"
|
|
||||||
>
|
|
||||||
Open settings <mdicon style="margin-left: 0.5rem;" name="open-in-new" size="16"></mdicon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<small>Controls will only work on content embedded from the following sites:</small><br/>
|
|
||||||
<div class="w-full flex flex-row justify-center">
|
|
||||||
<span v-for="host of enabledHosts" :key="host" class="website-name">{{host}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="16" />
|
|
||||||
<span>CROP</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="margin-top: -0.69rem; margin-bottom: 0.88rem;"
|
|
||||||
>
|
|
||||||
<CropOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:isEditing="false"
|
|
||||||
:compact="true"
|
|
||||||
>
|
|
||||||
</CropOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="16" />
|
|
||||||
<span>STRETCH</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="margin-top: -0.69rem; margin-bottom: 0.88rem;"
|
|
||||||
>
|
|
||||||
<StretchOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:isEditing="false"
|
|
||||||
:compact="true"
|
|
||||||
></StretchOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="16" />
|
|
||||||
<span>ZOOM</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="margin-top: -0.69rem; margin-bottom: 0.88rem;"
|
|
||||||
>
|
|
||||||
<ZoomOptionsPanel
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:isEditing="false"
|
|
||||||
:compact="true"
|
|
||||||
>
|
|
||||||
</ZoomOptionsPanel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<mdicon name="crop" :size="16" />
|
|
||||||
<span>ALIGN</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="margin-bottom: 0.88rem;"
|
|
||||||
>
|
|
||||||
<AlignmentOptionsControlComponent
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:large="true"
|
|
||||||
> </AlignmentOptionsControlComponent>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import CropOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/CropOptionsPanel';
|
|
||||||
import StretchOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/StretchOptionsPanel.vue';
|
|
||||||
import ZoomOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/ZoomOptionsPanel.vue';
|
|
||||||
import ExtensionMode from '@src/common/enums/ExtensionMode.enum.ts';
|
|
||||||
import AlignmentOptionsControlComponent from '@csui/src/PlayerUiPanels/AlignmentOptionsControlComponent.vue';
|
|
||||||
import { SiteSettings } from '../../../../ext/lib/settings/SiteSettings';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
CropOptionsPanel,
|
|
||||||
StretchOptionsPanel,
|
|
||||||
ZoomOptionsPanel,
|
|
||||||
AlignmentOptionsControlComponent
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'site',
|
|
||||||
'settings',
|
|
||||||
'siteSettings',
|
|
||||||
'eventBus',
|
|
||||||
'hosts'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
exec: null,
|
|
||||||
ExtensionMode: ExtensionMode,
|
|
||||||
enabledHosts: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
hosts(val) {
|
|
||||||
this.filterActiveSites(val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'uw-config-broadcast',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config) => this.handleConfigBroadcast(config)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.filterActiveSites(this.hosts);
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.eventBus.sendToTunnel('get-ar');
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribeAll(this);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
filterActiveSites(val) {
|
|
||||||
this.enabledHosts = [];
|
|
||||||
|
|
||||||
for (const host of val) {
|
|
||||||
const siteSettings = new SiteSettings(this.settings, {site: host});
|
|
||||||
|
|
||||||
if (siteSettings.isEnabledForEnvironment(false, true) === ExtensionMode.Enabled) {
|
|
||||||
this.enabledHosts.push(host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openSettings() {
|
|
||||||
this.eventBus.send('open-popup-settings', {tab: 'extensionSettings'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.warning-compact {
|
|
||||||
background-color: #d6ba4a;
|
|
||||||
color: #000;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
margin-top: -0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
.website-name {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
|
|
||||||
&:not(:last-of-type)::after {
|
|
||||||
content: ','
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,423 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col" style="padding-bottom: 20px; position: relative">
|
|
||||||
<!-- <div class="">
|
|
||||||
<div class="label">Player picker</div>
|
|
||||||
<div class="desc">
|
|
||||||
If extension doesn't detect player correctly, you can override it.
|
|
||||||
<small>(NOTE: this currently doesn't work for embedded videos)</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>Meaning of outlines:</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
<div class="pp_video flex flex-nogrow"><code><video></code> element</div>
|
|
||||||
<div class="pp_current flex flex-nogrow">Selected and not matched</div>
|
|
||||||
<div class="pp_matched flex flex-nogrow">Elements that match query selector</div>
|
|
||||||
<div class="pp_current_matched">Selected and matched, selector ok</div>
|
|
||||||
<div class="pp_match_children">Selected and matched, selector too vague</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<ShortcutButton label="Move up"
|
|
||||||
/>
|
|
||||||
<ShortcutButton label="Move down"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
<QsElement selector="#test_id" />
|
|
||||||
<QsElement selector=".test_class" />
|
|
||||||
<template v-for="qse of currentElementQs" >
|
|
||||||
<QsElement :selector="qse" :key="qse" />
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="label">
|
|
||||||
Player detection settings<br/><small>for {{site}}</small>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
If extension doesn't work, you can help a little by telling it where to look for the player.<br/>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<div class="">
|
|
||||||
<input :checked="playerManualQs"
|
|
||||||
@change="togglePlayerManualQs"
|
|
||||||
type="checkbox"
|
|
||||||
/> Manually specify player
|
|
||||||
<span v-if="playerManualQs" class="small-samecolor">
|
|
||||||
|
|
||||||
<a
|
|
||||||
v-if="playerByNodeIndex"
|
|
||||||
@click="toggleByNodeIndex"
|
|
||||||
>(use advanced options)</a>
|
|
||||||
<a
|
|
||||||
v-if="!playerByNodeIndex"
|
|
||||||
@click="toggleByNodeIndex"
|
|
||||||
>(use basic options)</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="playerManualQs">
|
|
||||||
<div v-if="!playerByNodeIndex" class="flex flex-col">
|
|
||||||
<div class="">Query selectors for player:</div>
|
|
||||||
<input type="text"
|
|
||||||
v-model="playerQs"
|
|
||||||
@change="updatePlayerQuerySelector"
|
|
||||||
@blur="updatePlayerQuerySelector"
|
|
||||||
:disabled="playerByNodeIndex || !playerManualQs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="playerByNodeIndex" class="flex flex-row">
|
|
||||||
<div>
|
|
||||||
Player is n-th parent of video:
|
|
||||||
</div>
|
|
||||||
<input v-model="playerParentNodeIndex"
|
|
||||||
:disabled="!playerManualQs"
|
|
||||||
@change="updatePlayerParentNodeIndex"
|
|
||||||
@blur="updatePlayerParentNodeIndex"
|
|
||||||
type="number"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="description">
|
|
||||||
<small>
|
|
||||||
<b>Hint:</b> Player is a HTML element that represents the portion of the page you expect the video to play in.
|
|
||||||
The correct value for n-th player value should normally between 1-10 (depends on the site). If none of these
|
|
||||||
values works, then the site probably has a different issue.
|
|
||||||
Note that you need to save settings and reload the page every time you change the number.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<input :checked="usePlayerAr"
|
|
||||||
@change="toggleUsePlayerAr"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
Do not use monitor AR in fullscreen
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="description">
|
|
||||||
<small>
|
|
||||||
<b>Hint:</b> When in full screen, the extension will assume that player element is as big as your screen.
|
|
||||||
You generally want to keep this option off, unless you like to browse in fullscreen a lot.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="label">
|
|
||||||
Additional css<br/><small>for {{site}}</small>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
This css will be inserted into webpage every time it loads.
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<textarea
|
|
||||||
v-model="playerCss"
|
|
||||||
@change="updatePlayerCss"
|
|
||||||
@blur="updatePlayerCss"
|
|
||||||
>
|
|
||||||
</textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="label">
|
|
||||||
Video detection settings<br/><small>for {{site}}</small>
|
|
||||||
</div>
|
|
||||||
<div class="description">Video is just the moving picture bit without the player.</div>
|
|
||||||
<div class="">
|
|
||||||
<div class="">
|
|
||||||
<input :checked="!videoManualQs"
|
|
||||||
@change="toggleVideoManualQs"
|
|
||||||
type="checkbox"
|
|
||||||
/> Detect automatically
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="flex label-secondary form-label">Query selectors</div>
|
|
||||||
<input type="text"
|
|
||||||
v-model="videoQs"
|
|
||||||
:disabled="!videoManualQs"
|
|
||||||
@change="updateVideoQuerySelector"
|
|
||||||
@blur="updateVideoQuerySelector"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="flex label-secondary form-label">Additional style for video element</div>
|
|
||||||
<input type="text"
|
|
||||||
v-model="videoCss"
|
|
||||||
@change="updateVideoCss"
|
|
||||||
@blur="updateVideoCss"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="label">
|
|
||||||
Browser quirk mitigations
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
Sometimes, the extension may misbehave as a result of issues and bugs present in your browser, operating system or your GPU driver.
|
|
||||||
Some of the issues can be fixed by limiting certain functionalities of this addon.
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div
|
|
||||||
v-if="BrowserDetect.anyChromium"
|
|
||||||
class="workaround flex flex-col"
|
|
||||||
>
|
|
||||||
<div class="flex label-secondary form-label">
|
|
||||||
<input :checked="settings?.active?.mitigations?.zoomLimit?.enabled"
|
|
||||||
@change="setMitigation(['zoomLimit', 'enabled'], $event.target.checked)"
|
|
||||||
type="checkbox"
|
|
||||||
/> Limit zoom.
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<div class="label-secondary form-label">
|
|
||||||
<small>Limit zoom to % of width (1=100%):</small>
|
|
||||||
</div>
|
|
||||||
<input type="number"
|
|
||||||
:value="settings?.active?.mitigations?.zoomLimit?.limit || 0.997"
|
|
||||||
step="0.001"
|
|
||||||
min="0.5"
|
|
||||||
@change="setMitigation(['zoomLimit', 'limit'], +$event.target.value)"
|
|
||||||
@blur="updateVideoQuerySelector"
|
|
||||||
:disabled="!settings?.active?.mitigations?.zoomLimit?.enabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex label-secondary form-label">
|
|
||||||
<input :checked="settings?.active?.mitigations?.zoomLimit?.fullscreenOnly"
|
|
||||||
@change="setMitigation(['zoomLimit', 'fullscreenOnly'], $event.target.checked)"
|
|
||||||
:disabled="!settings?.active?.mitigations?.zoomLimit?.enabled"
|
|
||||||
type="checkbox"
|
|
||||||
/> Limit zoom only while in fullscreen
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<small>
|
|
||||||
<b>Fix for:</b> Chrome and Edge used to have a bug where videos would get incorrectly stretched when zoomed in too far.
|
|
||||||
The issue only appeared in fullscreen, on nVidia GPUs, and with hardware acceleration enabled. While this option only
|
|
||||||
needs to be applied in fullscreen, fullscreen detection in Chrome can be a bit unreliable (depending on your OS and/or
|
|
||||||
display scaling settings). <a href="https://stuff.tamius.net/sacred-texts/2021/02/01/ultrawidify-and-chrome-2021-edition/" target="_blank">More about the issue</a>.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div> </div>
|
|
||||||
|
|
||||||
<div> </div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="save-banner-observer-bait">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id="save-banner"
|
|
||||||
class="save-banner"
|
|
||||||
>
|
|
||||||
<div class="button">Save settings</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ShortcutButton from '../../common/components/ShortcutButton.vue';
|
|
||||||
import QsElement from '../../common/components/QsElement.vue';
|
|
||||||
import QuerySelectorSetting from '../../common/components/QuerySelectorSetting.vue';
|
|
||||||
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
|
||||||
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
|
|
||||||
import StretchType from '../../common/enums/StretchType.enum';
|
|
||||||
import BrowserDetect from '../../ext/conf/BrowserDetect';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
QuerySelectorSetting,
|
|
||||||
ShortcutButton,
|
|
||||||
QsElement,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
videoManualQs: false,
|
|
||||||
videoQs: '',
|
|
||||||
videoCss: '',
|
|
||||||
playerManualQs: false,
|
|
||||||
playerQs: '',
|
|
||||||
playerCss: '',
|
|
||||||
playerByNodeIndex: true,
|
|
||||||
playerParentNodeIndex: undefined,
|
|
||||||
usePlayerAr: false,
|
|
||||||
BrowserDetect
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
site: String,
|
|
||||||
siteSettings: Object,
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
try {
|
|
||||||
this.videoManualQs = this.siteSettings.data.currentDOMConfig?.elements?.video?.manual ?? this.videoManualQs;
|
|
||||||
this.videoQs = this.siteSettings.data.currentDOMConfig?.elements?.video?.querySelectors;
|
|
||||||
this.videoCss = this.siteSettings.data.currentDOMConfig?.elements?.video?.nodeCss;
|
|
||||||
} catch (e) {
|
|
||||||
// that's here just in case relevant settings for this site don't exist yet
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.playerManualQs = this.siteSettings.data.currentDOMConfig?.elements?.player?.manual ?? this.playerManualQs;
|
|
||||||
this.playerQs = this.siteSettings.data.currentDOMConfig?.elements?.player?.querySelectors;
|
|
||||||
this.playerByNodeIndex = !this.siteSettings.data.currentDOMConfig?.elements?.player?.querySelectors || this.playerByNodeIndex;
|
|
||||||
this.playerParentNodeIndex = this.siteSettings.data.currentDOMConfig?.elements?.player?.index;
|
|
||||||
// this.usePlayerAr = this.settings.active.sites[this.site]?.usePlayerArInFullscreen;
|
|
||||||
} catch (e) {
|
|
||||||
// that's here just in case relevant settings for this site don't exist yet
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.playerCss = this.settings.active.sites[this.site].css || '';
|
|
||||||
} catch (e) {
|
|
||||||
// that's here just in case relevant settings for this site don't exist yet
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.createObserver();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
createObserver() {
|
|
||||||
const saveButtonBait = document.getElementById('save-banner-observer-bait');
|
|
||||||
const saveButton = document.getElementById('save-banner');
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
([e]) => {
|
|
||||||
saveButton.classList.toggle('floating', e.intersectionRatio < 0.95);
|
|
||||||
},
|
|
||||||
{threshold: [0, 0.5, 0.9, 0.95, 1]}
|
|
||||||
);
|
|
||||||
|
|
||||||
observer.observe(saveButtonBait);
|
|
||||||
},
|
|
||||||
updateVideoQuerySelector() {
|
|
||||||
this.siteSettings.set('currentDOMConfig')
|
|
||||||
this.ensureSettings('video');
|
|
||||||
this.settings.active.sites[this.site].DOM.video.querySelectors = this.videoQs;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
updateVideoCss() {
|
|
||||||
this.ensureSettings('video');
|
|
||||||
this.settings.active.sites[this.site].DOM.video.additionalCss = this.videoCss;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
updatePlayerQuerySelector() {
|
|
||||||
this.ensureSettings('player');
|
|
||||||
this.settings.active.sites[this.site].DOM.player.querySelectors = this.playerQs;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
updatePlayerCss() {
|
|
||||||
this.ensureSettings('player');
|
|
||||||
this.settings.active.sites[this.site].css = this.playerCss;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
updatePlayerParentNodeIndex() {
|
|
||||||
this.ensureSettings('player');
|
|
||||||
this.settings.active.sites[this.site].DOM.player.videoAncestor = this.playerParentNodeIndex;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
toggleVideoManualQs() {
|
|
||||||
this.ensureSettings('video');
|
|
||||||
this.videoManualQs = !this.videoManualQs;
|
|
||||||
this.settings.active.sites[this.site].DOM.video.manual = this.videoManualQs;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
togglePlayerManualQs() {
|
|
||||||
this.ensureSettings('player');
|
|
||||||
this.playerManualQs = !this.playerManualQs;
|
|
||||||
this.settings.active.sites[this.site].DOM.player.manual = this.playerManualQs;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
toggleByNodeIndex() {
|
|
||||||
this.ensureSettings('player');
|
|
||||||
this.playerByNodeIndex = !this.playerByNodeIndex;
|
|
||||||
this.settings.active.sites[this.site].DOM.player.useRelativeAncestor = this.playerByNodeIndex;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
toggleUsePlayerAr() {
|
|
||||||
this.ensureSettings('player');
|
|
||||||
this.usePlayerAr = !this.usePlayerAr;
|
|
||||||
this.settings.active.sites[this.site].usePlayerArInFullscreen = this.usePlayerAr;
|
|
||||||
this.settings.save();
|
|
||||||
},
|
|
||||||
setMitigation(mitigation, value) {
|
|
||||||
// ensure mitigations object exists.
|
|
||||||
// it may not exist in the settings on first load
|
|
||||||
if (! this.settings.active.mitigations) {
|
|
||||||
this.settings.active.mitigations = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(mitigation)) {
|
|
||||||
let currentMitigationsParent = this.settings.active.mitigations;
|
|
||||||
|
|
||||||
for (let i = 0; i < mitigation.length - 1; i++) {
|
|
||||||
if (!currentMitigationsParent[mitigation[i]]) {
|
|
||||||
currentMitigationsParent[mitigation[i]] = {};
|
|
||||||
}
|
|
||||||
currentMitigationsParent = currentMitigationsParent[mitigation[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
currentMitigationsParent[mitigation[mitigation.length - 1]] = value;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.settings.active.mitigations[mitigation] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../../res/css/colors.scss';
|
|
||||||
|
|
||||||
.pp_video {
|
|
||||||
margin: 2px;
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid #00f;
|
|
||||||
}
|
|
||||||
.pp_current {
|
|
||||||
margin: 2px;
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid #88f;
|
|
||||||
}
|
|
||||||
.pp_matched {
|
|
||||||
margin: 2px;
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px dashed #fd2;
|
|
||||||
}
|
|
||||||
.pp_current_matched {
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
border: 2px solid #027a5c;
|
|
||||||
}
|
|
||||||
.pp_match_children {
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
border: 2px solid #f00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-banner {
|
|
||||||
display: block;
|
|
||||||
position: sticky;
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
background-color: #131313;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&.floating {
|
|
||||||
box-shadow: 0 2rem 3rem 1rem $selected-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.small-samecolor {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,215 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-row" style="width: 250px;">
|
|
||||||
<div class="flex-grow">
|
|
||||||
Custom zoom
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<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 v-if="zoomAspectRatioLocked">
|
|
||||||
<div class="input range-input no-bg">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
class="slider"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
step="0.01"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value)"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
class="disabled"
|
|
||||||
style="width: 2rem;"
|
|
||||||
:value="getZoomForDisplay('x')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="top-label">Horizontal zoom:</div>
|
|
||||||
<div class="input range-input no-bg">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
class="slider"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
step="0.01"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value, 'x')"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
class="disabled"
|
|
||||||
style="width: 2rem;"
|
|
||||||
:value="getZoomForDisplay('x')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="top-label">Vertical zoom:</div>
|
|
||||||
<div class="input range-input no-bg">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
class="slider"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
step="0.01"
|
|
||||||
:value="zoom.y"
|
|
||||||
@input="changeZoom($event.target.value, 'y')"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
class="disabled"
|
|
||||||
style="width: 2rem;"
|
|
||||||
:value="getZoomForDisplay('y')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Button from '@csui/src/components/Button.vue';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Button,
|
|
||||||
|
|
||||||
},
|
|
||||||
mixins: [
|
|
||||||
|
|
||||||
],
|
|
||||||
props: [
|
|
||||||
'settings', // required for buttons and actions, which are global
|
|
||||||
'eventBus'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
zoomAspectRatioLocked: true,
|
|
||||||
zoom: {
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: this should be mixin?
|
|
||||||
resizerConfig: {
|
|
||||||
crop: null,
|
|
||||||
stretch: null,
|
|
||||||
zoom: null,
|
|
||||||
pan: null
|
|
||||||
},
|
|
||||||
pollingInterval: undefined,
|
|
||||||
debouncedGetEffectiveZoom: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.eventBus?.subscribeMulti(
|
|
||||||
{
|
|
||||||
'announce-zoom': {
|
|
||||||
function: (data) => {
|
|
||||||
this.zoom = {
|
|
||||||
x: Math.log2(data.x),
|
|
||||||
y: Math.log2(data.y)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
this
|
|
||||||
);
|
|
||||||
this.debouncedGetEffectiveZoom = _.debounce(
|
|
||||||
() => {
|
|
||||||
this.getEffectiveZoom();
|
|
||||||
},
|
|
||||||
250
|
|
||||||
),
|
|
||||||
this.getEffectiveZoom();
|
|
||||||
this.pollingInterval = setInterval(this.debouncedGetEffectiveZoom, 2000);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribe(this);
|
|
||||||
clearInterval(this.pollingInterval);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getEffectiveZoom() {
|
|
||||||
this.eventBus?.sendToTunnel('get-effective-zoom', {});
|
|
||||||
},
|
|
||||||
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, isLinear) {
|
|
||||||
if (isNaN(+newZoom)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let logZoom, linZoom;
|
|
||||||
if (isLinear) {
|
|
||||||
newZoom /= 100;
|
|
||||||
logZoom = Math.log2(newZoom);
|
|
||||||
linZoom = newZoom;
|
|
||||||
} else {
|
|
||||||
logZoom = newZoom;
|
|
||||||
linZoom = Math.pow(2, newZoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we store zoom logarithmically on this component
|
|
||||||
if (!axis) {
|
|
||||||
this.zoom.x = logZoom;
|
|
||||||
} else {
|
|
||||||
this.zoom[axis] = logZoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we do not use logarithmic zoom elsewhere, therefore we need to convert
|
|
||||||
|
|
||||||
if (this.zoomAspectRatioLocked) {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: linZoom});
|
|
||||||
} else {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: {[axis ?? 'x']: linZoom}});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" src="@csui/res/css/flex.scss" scoped></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,9 +0,0 @@
|
|||||||
$warning-color: #d6ba4a;
|
|
||||||
|
|
||||||
$primary: rgb(255, 147, 85);
|
|
||||||
$primaryBrighter: rgb(255, 174, 127);
|
|
||||||
$primaryBg: rgba(54, 31, 21, 0.5);
|
|
||||||
|
|
||||||
$blackBg: rgb(11,11,11);
|
|
||||||
$normalTransparentOpacity: 0.5;
|
|
||||||
$hoverTransparentOpacity: 0.9;
|
|
||||||
@ -1,263 +0,0 @@
|
|||||||
@import './_variables.scss';
|
|
||||||
|
|
||||||
div, p, span {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
/* color: #666; */
|
|
||||||
filter: contrast(50%) brightness(40%) grayscale(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-box {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
border: 1px solid $warning-color;
|
|
||||||
color: $warning-color;
|
|
||||||
margin: 0px 16px;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
font-size: 16;
|
|
||||||
|
|
||||||
border-radius: 2px;
|
|
||||||
|
|
||||||
*:not(:first-child) {
|
|
||||||
margin-left: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3 {
|
|
||||||
font-family: 'Overpass' !important;
|
|
||||||
font-weight: 100 !important;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button, .button {
|
|
||||||
background-color: rgba($blackBg, $normalTransparentOpacity);
|
|
||||||
|
|
||||||
padding: 0.5rem 2rem;
|
|
||||||
margin: 3px;
|
|
||||||
|
|
||||||
color: #aaa;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
user-select: none !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: rgba($blackBg, $hoverTransparentOpacity);
|
|
||||||
border-bottom: 1px solid rgba($primary, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $primary;
|
|
||||||
background-color: $primaryBg;
|
|
||||||
border-color: rgba($primary, .5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&.primary {
|
|
||||||
background-color: #fa6;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
background-color: #ff2211 !important;
|
|
||||||
color:#000;
|
|
||||||
}
|
|
||||||
&.disabled {
|
|
||||||
filter: saturate(0%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.b3 {
|
|
||||||
margin: 0.25rem;
|
|
||||||
padding: 0.5rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input, .range-input {
|
|
||||||
background-color: rgba($blackBg, $normalTransparentOpacity);
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.5);
|
|
||||||
padding-top: 0.25rem;
|
|
||||||
padding-bottom: 0.25rem;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:active, &:focus, &:focus-within {
|
|
||||||
border-bottom: 1px solid rgba($primary, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-bg {
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
outline: none;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit {
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.69;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
top: 0;
|
|
||||||
transform: translateY(69%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.range-input {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
input {
|
|
||||||
max-width: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=range] {
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.l2 {
|
|
||||||
margin-left: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
flex: 0 0 25%;
|
|
||||||
min-width: 16rem;
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 1rem;
|
|
||||||
|
|
||||||
.color-emphasis {
|
|
||||||
color: #fa6;
|
|
||||||
}
|
|
||||||
.sub-label {
|
|
||||||
font-size: 0.9em;
|
|
||||||
opacity: 0.69;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input, .range-input {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
max-width: 24rem;
|
|
||||||
min-width: 16rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-hint {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
min-width: 12px;
|
|
||||||
max-width: 24rem;
|
|
||||||
|
|
||||||
select {
|
|
||||||
background: rgba($blackBg, $hoverTransparentOpacity);
|
|
||||||
|
|
||||||
min-width: 12px;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
border: 1px solid rgba(255, 171, 102, 0.42);
|
|
||||||
|
|
||||||
padding: 0.5rem 1rem 0.25rem;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
font: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
margin-left: 5rem;
|
|
||||||
box-sizing:border-box;
|
|
||||||
|
|
||||||
&.error {
|
|
||||||
color: #f41;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.options-bar {
|
|
||||||
position: absolute;
|
|
||||||
width: calc(100% - 4rem);
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
&.isEditing {
|
|
||||||
background-color: $primary;
|
|
||||||
position: relative;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.b3 {
|
|
||||||
width: 9rem;
|
|
||||||
padding-left: 0.33rem;
|
|
||||||
padding-right: 0.33rem;
|
|
||||||
}
|
|
||||||
.b3-compact {
|
|
||||||
width: 7rem;
|
|
||||||
padding-left: 0.25rem;
|
|
||||||
padding-right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-lite {
|
|
||||||
padding-right: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-action-area {
|
|
||||||
background-color: rgba($blackBg,0.5);
|
|
||||||
padding: 0.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
.edit-action-area-header {
|
|
||||||
background-color: $primary;
|
|
||||||
color: #000;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
.sub-panel {
|
|
||||||
max-width: 52rem;
|
|
||||||
margin: 1rem;
|
|
||||||
|
|
||||||
h1:not(:first-child) {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sub-panel-content {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
border-bottom: 1px solid black;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
|
|
||||||
&:hover, &.active {
|
|
||||||
border-bottom: 1px solid #fa6;
|
|
||||||
background-color:rgba(0, 0, 0, 0.75)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
@ -156,15 +156,6 @@
|
|||||||
</About>
|
</About>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<PlayerDetectionPanel
|
|
||||||
v-if="selectedTab === 'playerDetection'"
|
|
||||||
:siteSettings="siteSettings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
:site="site"
|
|
||||||
>
|
|
||||||
</PlayerDetectionPanel>
|
|
||||||
|
|
||||||
<ImportExportSettings v-if="selectedTab === 'import-export-settings'"
|
<ImportExportSettings v-if="selectedTab === 'import-export-settings'"
|
||||||
:settings="settings"
|
:settings="settings"
|
||||||
>
|
>
|
||||||
@ -204,9 +195,6 @@ import AfterUpdate from '@components/segments/AfterUpdate/AfterUpdate.vue';
|
|||||||
import WhatsNew from '@components/segments/ExtensionInfo/WhatsNew.vue';
|
import WhatsNew from '@components/segments/ExtensionInfo/WhatsNew.vue';
|
||||||
import About from '@components/segments/ExtensionInfo/About.vue';
|
import About from '@components/segments/ExtensionInfo/About.vue';
|
||||||
|
|
||||||
// to replace:
|
|
||||||
import PlayerDetectionPanel from '../../csui/src/PlayerUiPanels/PlayerDetectionPanel.vue'
|
|
||||||
|
|
||||||
// not component:
|
// not component:
|
||||||
import BrowserDetect from '@src/ext/conf/BrowserDetect'
|
import BrowserDetect from '@src/ext/conf/BrowserDetect'
|
||||||
|
|
||||||
@ -279,8 +267,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
WhatsNew,
|
WhatsNew,
|
||||||
About,
|
About,
|
||||||
|
|
||||||
PlayerDetectionPanel,
|
|
||||||
},
|
},
|
||||||
mixins: [],
|
mixins: [],
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@ -188,13 +188,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import Popup from '@components/common/Popup.vue';
|
import Popup from '@components/common/Popup.vue';
|
||||||
import JsonEditor from '@components/common/JsonEditor.vue';
|
import JsonEditor from '@components/common/JsonEditor.vue';
|
||||||
import UploadJsonFileButton from '@components/common/UploadJsonFileButton.vue';
|
import UploadJsonFileButton from '@components/common/UploadJsonFileButton.vue';
|
||||||
|
|
||||||
import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/lib/logging/LogAggregator';
|
import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/lib/logging/LogAggregator';
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { SettingsSnapshot } from '@src/ext/lib/settings/SettingsSnapshotManager';
|
import { SettingsSnapshot } from '@src/ext/lib/settings/SettingsSnapshotManager';
|
||||||
import SettingsInterface from '../../../../common/interfaces/SettingsInterface';
|
import SettingsInterface from '@src/common/interfaces/SettingsInterface';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -155,18 +155,11 @@ import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
|
|||||||
import StretchType from '@src/common/enums/StretchType.enum';
|
import StretchType from '@src/common/enums/StretchType.enum';
|
||||||
import NewAspectRatioForm from './Components/NewAspectRatioForm.vue';
|
import NewAspectRatioForm from './Components/NewAspectRatioForm.vue';
|
||||||
|
|
||||||
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 defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
KeyboardShortcutEntry,
|
KeyboardShortcutEntry,
|
||||||
NewAspectRatioForm,
|
NewAspectRatioForm,
|
||||||
|
|
||||||
CropOptionsPanel,
|
|
||||||
StretchOptionsPanel,
|
|
||||||
ZoomOptionsPanel,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<!-- ADVANCED OPTIONS -->
|
||||||
|
<pre>{{siteSettings.raw}}</pre>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default({
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@ -0,0 +1,261 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full flex flex-row" style="margin-top: 1rem;">
|
||||||
|
|
||||||
|
<!-- PLAYER ELEMENT SELECTOR FOR DUMMIES -->
|
||||||
|
<div style="width: 48%">
|
||||||
|
<div class="sub-panel-content">
|
||||||
|
|
||||||
|
<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(elementStack.length - index - 1, true)"
|
||||||
|
@mouseleave="markElement(elementStack.length - index - 1, false)"
|
||||||
|
|
||||||
|
@click="setPlayer(elementStack.length - index - 1)"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="element-config">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="css-preview">
|
||||||
|
{{cssStack}}
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
elementStack: [],
|
||||||
|
cssStack: [],
|
||||||
|
showLegend: false,
|
||||||
|
showAdvancedOptions: false,
|
||||||
|
tutorialVisible: false,
|
||||||
|
tutorialStep: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showTutorial() {
|
||||||
|
this.tutorialVisible = true;
|
||||||
|
this.tutorialStep = 0;
|
||||||
|
},
|
||||||
|
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.reverse();
|
||||||
|
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>
|
||||||
@ -88,213 +88,11 @@
|
|||||||
<mdicon name="help"></mdicon>
|
<mdicon name="help"></mdicon>
|
||||||
How do I use this?
|
How do I use this?
|
||||||
</button>
|
</button>
|
||||||
<div class="w-full flex flex-row" style="margin-top: 1rem;">
|
|
||||||
|
|
||||||
<!-- PLAYER ELEMENT SELECTOR FOR DUMMIES -->
|
|
||||||
<div style="width: 48%">
|
|
||||||
<div class="sub-panel-content">
|
|
||||||
<h2>Simple player selector</h2>
|
|
||||||
|
|
||||||
|
|
||||||
<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(elementStack.length - index - 1, true)"
|
|
||||||
@mouseleave="markElement(elementStack.length - index - 1, false)"
|
|
||||||
|
|
||||||
@click="setPlayer(elementStack.length - index - 1)"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="element-config">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="css-preview">
|
|
||||||
{{cssStack}}
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ADVANCED OPTIONS -->
|
|
||||||
<div style="width: 48%" v-if="false">
|
|
||||||
<h2>Advanced options</h2>
|
|
||||||
|
|
||||||
<pre>{{siteSettings.raw}}</pre>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script lang="ts">
|
||||||
export default({
|
export default({
|
||||||
components: {
|
components: {
|
||||||
|
|
||||||
@ -334,7 +132,6 @@ export default({
|
|||||||
destroyed() {
|
destroyed() {
|
||||||
this.eventBus.unsubscribeAll(this);
|
this.eventBus.unsubscribeAll(this);
|
||||||
},
|
},
|
||||||
computed: {},
|
|
||||||
methods: {
|
methods: {
|
||||||
showTutorial() {
|
showTutorial() {
|
||||||
this.tutorialVisible = true;
|
this.tutorialVisible = true;
|
||||||
@ -112,16 +112,10 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import BrowserDetect from '@src/ext/conf/BrowserDetect';
|
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'
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
CropOptionsPanel,
|
|
||||||
StretchOptionsPanel,
|
|
||||||
ZoomOptionsPanel,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -17,8 +17,6 @@ const config = {
|
|||||||
entry: {
|
entry: {
|
||||||
'ext/uw': './ext/uw.js',
|
'ext/uw': './ext/uw.js',
|
||||||
'uw-bg': './uw-bg.js',
|
'uw-bg': './uw-bg.js',
|
||||||
'csui/csui-popup': './csui/csui-popup.js',
|
|
||||||
|
|
||||||
'ui/pages/settings/settings': './ui/pages/settings/settings.js',
|
'ui/pages/settings/settings': './ui/pages/settings/settings.js',
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@ -119,10 +117,6 @@ const config = {
|
|||||||
// This folder does not contain any GUI icons — these are in /res/icons.
|
// This folder does not contain any GUI icons — these are in /res/icons.
|
||||||
// (TODO: check if this copy is even necessary — /icons has same content as /res/icons)
|
// (TODO: check if this copy is even necessary — /icons has same content as /res/icons)
|
||||||
{ from: 'icons', to: 'icons', globOptions: { ignore: ['icon.xcf'] } },
|
{ from: 'icons', to: 'icons', globOptions: { ignore: ['icon.xcf'] } },
|
||||||
{ from: 'csui/csui-popup.html', to: 'csui/csui-popup.html', transform: transformHtml },
|
|
||||||
{ from: 'csui/csui-overlay-normal.html', to: 'csui/csui.html', transform: transformHtml },
|
|
||||||
{ from: 'csui/csui-overlay-dark.html', to: 'csui/csui-dark.html', transform: transformHtml },
|
|
||||||
{ from: 'csui/csui-overlay-light.html', to: 'csui/csui-light.html', transform: transformHtml },
|
|
||||||
{ from: 'ui/pages/settings/index.html', to: 'ui/pages/settings/index.html', transform: transformHtml },
|
{ from: 'ui/pages/settings/index.html', to: 'ui/pages/settings/index.html', transform: transformHtml },
|
||||||
{
|
{
|
||||||
from: 'manifest.json',
|
from: 'manifest.json',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user