Popup for setting site-specific options is roughly done
This commit is contained in:
parent
17f374d0c3
commit
03d8a99885
87
src/common/components/QuerySelectorSetting.vue
Normal file
87
src/common/components/QuerySelectorSetting.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-column">
|
||||||
|
<div v-if="!editing && !adding" class="flex flex-row">
|
||||||
|
<div class="">
|
||||||
|
<b>Query selector:</b> {{qs.string}}<br/>
|
||||||
|
<b>Additional CSS:</b> {{qs.css || 'no style rules'}}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-column flex-nogrow">
|
||||||
|
<a @click="editing = true">Edit</a>
|
||||||
|
<a @click="$emit('delete')">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex flex-row">
|
||||||
|
<div class="flex flex-column">
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="flex label-secondary form-label">
|
||||||
|
Query selector:
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input type="text"
|
||||||
|
v-model="qs.string"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="flex label-secondary form-label">
|
||||||
|
Additional CSS:
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input type="text"
|
||||||
|
v-model="qs.css"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a v-if="editing" @click="cancelEdit">Cancel</a>
|
||||||
|
<a v-if="adding" @click="clear">Clear</a>
|
||||||
|
<a @click="save">Save</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
qs: {string: '', css: ''},
|
||||||
|
editing: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
querySelector: Object,
|
||||||
|
adding: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
querySelector(val) {
|
||||||
|
this.qs = JSON.parse(JSON.stringify(val));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
save() {
|
||||||
|
if (this.adding) {
|
||||||
|
this.$emit('create', this.qs);
|
||||||
|
this.qs = {string: '', css: ''};
|
||||||
|
} else {
|
||||||
|
this.emit$('update', this.qs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancelEdit() {
|
||||||
|
this.qs = JSON.parse(JSON.stringify(this.querySelector));
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
this.qs = {string: '', css: ''};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
@ -1,18 +1,18 @@
|
|||||||
// Set prod to true when releasing
|
// Set prod to true when releasing
|
||||||
const _prod = true;
|
// const _prod = true;
|
||||||
// const _prod = false;
|
const _prod = false;
|
||||||
|
|
||||||
var Debug = {
|
var Debug = {
|
||||||
// performanceMetrics: true, // should not be affected by debug.debug in order to allow benchmarking of the impact logging in console has
|
// performanceMetrics: true, // should not be affected by debug.debug in order to allow benchmarking of the impact logging in console has
|
||||||
// init: true,
|
// init: true,
|
||||||
// debug: true,
|
debug: true,
|
||||||
// debug: false,
|
// debug: false,
|
||||||
// keyboard: true,
|
// keyboard: true,
|
||||||
// debugResizer: true,
|
// debugResizer: true,
|
||||||
// debugArDetect: true,
|
// debugArDetect: true,
|
||||||
// scaler: true,
|
// scaler: true,
|
||||||
// debugStorage: false,
|
// debugStorage: false,
|
||||||
// debugStorage: true,
|
debugStorage: true,
|
||||||
// comms: false,
|
// comms: false,
|
||||||
// comms: true,
|
// comms: true,
|
||||||
// showArDetectCanvas: true,
|
// showArDetectCanvas: true,
|
||||||
|
@ -927,17 +927,7 @@ var ExtensionConf = {
|
|||||||
stretch: Stretch.Default,
|
stretch: Stretch.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignment.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
// videoElement: { // extra stuff for video tag
|
|
||||||
// querySelectors: [], // array of strings with css selectors
|
|
||||||
// userCss: [], // additional styles that user can define for video element
|
|
||||||
// },
|
|
||||||
playerElement: {
|
|
||||||
// querySelectors: [], // array of strings with css selectors
|
|
||||||
videoAncestor: 1, // if not falsey, the number represents how far up the DOM (in nodes)
|
|
||||||
// from video the player lies. Can also be object (valid properties are
|
|
||||||
// 'fullscreen', 'embed' and 'normal')
|
|
||||||
userCss: [],
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,14 @@ class Settings {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Debug.debug && Debug.debugStorage) {
|
||||||
|
try {
|
||||||
|
console.log("[Settings::get] Got settings:", JSON.parse(ret.uwSettings));
|
||||||
|
} catch (e) {
|
||||||
|
console.log("[Settings::get] No settings.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return JSON.parse(ret.uwSettings);
|
return JSON.parse(ret.uwSettings);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
@ -151,7 +159,7 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async set(extensionConf) {
|
async set(extensionConf) {
|
||||||
if (Debug.debug) {
|
if (Debug.debug && Debug.debugStorage) {
|
||||||
console.log("[Settings::set] setting new settings:", extensionConf)
|
console.log("[Settings::set] setting new settings:", extensionConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +180,7 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (Debug.debug) {
|
if (Debug.debug && Debug.storage) {
|
||||||
console.log("[Settings::save] Saving active settings:", this.active);
|
console.log("[Settings::save] Saving active settings:", this.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<div class="">
|
<div class="">
|
||||||
Site settings
|
Site settings
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedTab === 'site' && this.activeFrames.length > 1"
|
<div v-if="selectedTab === 'site' && this.activeSites.length > 1"
|
||||||
class=""
|
class=""
|
||||||
>
|
>
|
||||||
<small>Select site to control:</small>
|
<small>Select site to control:</small>
|
||||||
@ -64,6 +64,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="menu-item"
|
||||||
|
:class="{'selected-tab': selectedTab === 'site-details'}"
|
||||||
|
@click="selectTab('site-details')"
|
||||||
|
>
|
||||||
|
<div class="">
|
||||||
|
Video and player detection
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'site-details' && this.activeSites.length > 1"
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
<small>Select site to control:</small>
|
||||||
|
<div class="">
|
||||||
|
<div v-for="site of activeSites"
|
||||||
|
:key="site.host"
|
||||||
|
class="tabitem"
|
||||||
|
:class="{'tabitem-selected': site.host === selectedSite}"
|
||||||
|
@click="selectSite(site.host)"
|
||||||
|
>
|
||||||
|
{{site.host}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="menu-item"
|
<div class="menu-item"
|
||||||
:class="{'selected-tab': selectedTab === 'about'}"
|
:class="{'selected-tab': selectedTab === 'about'}"
|
||||||
@click="selectTab('about')"
|
@click="selectTab('about')"
|
||||||
@ -101,6 +125,11 @@
|
|||||||
:scope="selectedTab"
|
:scope="selectedTab"
|
||||||
:site="selectedSite"
|
:site="selectedSite"
|
||||||
/>
|
/>
|
||||||
|
<SiteDetailsPanel v-if="settings && settings.active && selectedTab === 'site-details' "
|
||||||
|
class=""
|
||||||
|
:settings="settings"
|
||||||
|
:site="selectedSite"
|
||||||
|
/>
|
||||||
<PerformancePanel v-if="selectedTab === 'performance-metrics'"
|
<PerformancePanel v-if="selectedTab === 'performance-metrics'"
|
||||||
:performance="performance" />
|
:performance="performance" />
|
||||||
<AboutPanel v-if="selectedTab === 'about'" />
|
<AboutPanel v-if="selectedTab === 'about'" />
|
||||||
@ -111,6 +140,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import SiteDetailsPanel from './panels/SiteDetailsPanel.vue';
|
||||||
import Donate from '../common/misc/Donate.vue';
|
import Donate from '../common/misc/Donate.vue';
|
||||||
import Debug from '../ext/conf/Debug';
|
import Debug from '../ext/conf/Debug';
|
||||||
import BrowserDetect from '../ext/conf/BrowserDetect';
|
import BrowserDetect from '../ext/conf/BrowserDetect';
|
||||||
@ -171,6 +201,7 @@ export default {
|
|||||||
Debug,
|
Debug,
|
||||||
AboutPanel,
|
AboutPanel,
|
||||||
Donate,
|
Donate,
|
||||||
|
SiteDetailsPanel,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async sleep(t) {
|
async sleep(t) {
|
||||||
@ -231,8 +262,7 @@ export default {
|
|||||||
// host: message.site.host,
|
// host: message.site.host,
|
||||||
// isIFrame: false, // currently unused
|
// isIFrame: false, // currently unused
|
||||||
// });
|
// });
|
||||||
this.selectedSite = message.site.host.host;
|
this.selectedSite = this.selectedSite || message.site.host;
|
||||||
|
|
||||||
|
|
||||||
// loadConfig(site.host); TODO
|
// loadConfig(site.host); TODO
|
||||||
this.loadFrames(this.site);
|
this.loadFrames(this.site);
|
||||||
@ -282,7 +312,7 @@ export default {
|
|||||||
host: this.site.host,
|
host: this.site.host,
|
||||||
isIFrame: false, // not used tho. Maybe one day
|
isIFrame: false, // not used tho. Maybe one day
|
||||||
}];
|
}];
|
||||||
this.selectedSite = this.site.host;
|
this.selectedSite = this.selectedSite || this.site.host;
|
||||||
|
|
||||||
for (const frame in videoTab.frames) {
|
for (const frame in videoTab.frames) {
|
||||||
this.activeFrames.push({
|
this.activeFrames.push({
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<div v-if="settings && extensionActions.length"
|
<div v-if="settings && extensionActions.length"
|
||||||
class="w100"
|
class="w100"
|
||||||
>
|
>
|
||||||
<div class="label">Enable extension {{scope === 'site' ? 'for this site' : ''}}:</div>
|
<div class="label">Enable extension <template v-if="scope === 'site'">for {{site}}</template>:</div>
|
||||||
<div class="flex flex-row flex-wrap">
|
<div class="flex flex-row flex-wrap">
|
||||||
<ShortcutButton v-for="(action, index) of extensionActions"
|
<ShortcutButton v-for="(action, index) of extensionActions"
|
||||||
class="flex flex-grow button"
|
class="flex flex-grow button"
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<div v-if="aardActions.length"
|
<div v-if="aardActions.length"
|
||||||
class="w100"
|
class="w100"
|
||||||
>
|
>
|
||||||
<div class="label">Enable autodetection {{scope === 'site' ? 'for this site' : ''}}:</div>
|
<div class="label">Enable autodetection <template v-if="scope === 'site'">for {{site}}</template>:</div>
|
||||||
<div class="flex flex-row flex-wrap">
|
<div class="flex flex-row flex-wrap">
|
||||||
<ShortcutButton v-for="(action, index) of aardActions"
|
<ShortcutButton v-for="(action, index) of aardActions"
|
||||||
class="flex flex-grow button"
|
class="flex flex-grow button"
|
||||||
|
225
src/popup/panels/SiteDetailsPanel.vue
Normal file
225
src/popup/panels/SiteDetailsPanel.vue
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w100 flex flex-column" style="padding-bottom: 20px">
|
||||||
|
<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="indent">
|
||||||
|
<div class="flex flex-row row-padding">
|
||||||
|
<div class="flex label-secondary form-label">Manually specify video element</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input :checked="videoManualQs"
|
||||||
|
@change="toggleVideoManualQs"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-column">
|
||||||
|
<div class="flex label-secondary form-label">Query selectors</div>
|
||||||
|
<QuerySelectorSetting v-for="(qs, index) of siteVideoQuerySelectors"
|
||||||
|
:disabled="!videoManualQs"
|
||||||
|
:key="index"
|
||||||
|
@update="updateQuerySelector('video', index, $event)"
|
||||||
|
@delete="deleteQuerySelector('video', index)"
|
||||||
|
/>
|
||||||
|
<div class="flex label-secondary form-label">Add new:</div>
|
||||||
|
<QuerySelectorSetting v-if="videoManualQs"
|
||||||
|
adding
|
||||||
|
@create="addQuerySelector('video', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="label">Player detection settings<br/><small>for {{site}}</small></div>
|
||||||
|
<div class="description">Player is the frame around the video. Extension crops/stretches the video to fit the player.</div>
|
||||||
|
<div class="indent">
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="flex label-secondary form-label">Detect automatically</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input :checked="playerManualQs"
|
||||||
|
@change="togglePlayerManualQs"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="flex label-secondary form-label">Specify player node parent index instead of query selector</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input :checked="playerByNodeIndex"
|
||||||
|
@change="toggleByNodeIndex"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-column">
|
||||||
|
<div class="flex label-secondary form-label">Query selectors</div>
|
||||||
|
<QuerySelectorSetting v-for="(qs, index) of sitePlayerQuerySelectors"
|
||||||
|
:disabled="!playerManualQs || playerByNodeIndex"
|
||||||
|
:key="index"
|
||||||
|
@update="updateQuerySelector('video', index, $event)"
|
||||||
|
@delete="deleteQuerySelector('video', index)"
|
||||||
|
/>
|
||||||
|
<div class="flex label-secondary form-label">Add new:</div>
|
||||||
|
<QuerySelectorSetting v-if="videoManualQs"
|
||||||
|
adding
|
||||||
|
@create="addQuerySelector('video', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div v-if="playerByNodeIndex">
|
||||||
|
<div class="flex flex-row row-padding">
|
||||||
|
<div class="flex label-secondary form-label">Player node parent index</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input :value="playerByNodeIndex"
|
||||||
|
@change="toggleByNodeIndex"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row row-padding">
|
||||||
|
<div class="flex label-secondary form-label">Player node css</div>
|
||||||
|
<div class="flex flex-input">
|
||||||
|
<input :value="playerByNodeIndex"
|
||||||
|
@change="toggleByNodeIndex"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import QuerySelectorSetting from '../../common/components/QuerySelectorSetting.vue';
|
||||||
|
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
||||||
|
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
||||||
|
import Stretch from '../../common/enums/stretch.enum';
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
QuerySelectorSetting,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
site: String,
|
||||||
|
settings: Object,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
siteVideoQuerySelectors() {
|
||||||
|
try {
|
||||||
|
return settings.active.sites[this.site].DOM.video.querySelectors;
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sitePlayerQuerySelectors() {
|
||||||
|
try {
|
||||||
|
return settings.active.sites[this.site].DOM.player.querySelectors;
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
videoManualQs: function() {
|
||||||
|
try {
|
||||||
|
console.log("this.settings.active.sites[this.site].DOM.video.enabled", this.settings.active.sites[this.site].DOM.video.enabled)
|
||||||
|
return this.settings.active.sites[this.site].DOM.video.enabled
|
||||||
|
} catch (e) {
|
||||||
|
console.log("e",e)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playerManualQs() {
|
||||||
|
try {
|
||||||
|
return this.settings.active.sites[this.site].DOM.player.enabled
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playerByNodeIndex() {
|
||||||
|
try {
|
||||||
|
return this.settings.active.sites[this.site].DOM.player.byNodeIndex
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playerNodeIndex() {
|
||||||
|
try {
|
||||||
|
return this.settings.active.sites[this.site].DOM.player.nodeIndex
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playerNodeIndexCss() {
|
||||||
|
try {
|
||||||
|
return this.settings.active.sites[this.site].DOM.player.nodeIndexCss
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
ensureSettings(scope) {
|
||||||
|
if (! this.settings.active.sites[this.site]) {
|
||||||
|
this.settings.active.sites[this.site] = {
|
||||||
|
mode: ExtensionMode.Default,
|
||||||
|
autoar: ExtensionMode.Default,
|
||||||
|
type: 'user-added',
|
||||||
|
stretch: Stretch.Default,
|
||||||
|
videoAlignment: VideoAlignment.Default,
|
||||||
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! this.settings.active.sites[this.site].DOM) {
|
||||||
|
this.settings.active.sites[this.site].DOM = {};
|
||||||
|
}
|
||||||
|
if (! this.settings.active.sites[this.site].DOM[scope]) {
|
||||||
|
this.settings.active.sites[this.site].DOM[scope] = {
|
||||||
|
enabled: false,
|
||||||
|
querySelectors: [],
|
||||||
|
byNodeIndex: scope === 'player' ? false : undefined,
|
||||||
|
nodeIndex: undefined,
|
||||||
|
nodeIndexCss: scope === 'player' ? '' : undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateQuerySelector(scope, index, $event) {
|
||||||
|
this.ensureSettings(scope);
|
||||||
|
this.settings.active.sites[this.site].DOM[scope].querySelectors[index] = $event;
|
||||||
|
this.settings.save();
|
||||||
|
},
|
||||||
|
addQuerySelector(scope, index, $event) {
|
||||||
|
this.ensureSettings(scope);
|
||||||
|
this.settings.active.sites[this.site].DOM[scope].querySelectors.push($event);
|
||||||
|
},
|
||||||
|
deleteQuerySelector(scope, index) {
|
||||||
|
this.settings.active.sites[this.site].DOM[scope].querySelectors.splice(index, 1);
|
||||||
|
},
|
||||||
|
toggleVideoManualQs($event) {
|
||||||
|
this.ensureSettings('video');
|
||||||
|
this.settings.active.sites[this.site].DOM.video.enabled = !this.settings.active.sites[this.site].DOM.video.enabled;
|
||||||
|
this.settings.save();
|
||||||
|
},
|
||||||
|
togglePlayerManualQs($event) {
|
||||||
|
this.ensureSettings('player');
|
||||||
|
this.settings.active.sites[this.site].DOM.player.enabled = !this.settings.active.sites[this.site].DOM.player.enabled;
|
||||||
|
this.settings.save();
|
||||||
|
},
|
||||||
|
toggleByNodeIndex($event) {
|
||||||
|
this.ensureSettings('player');
|
||||||
|
this.settings.active.sites[this.site].DOM.player.byNodeIndex = !this.settings.active.sites[this.site].DOM.player.byNodeIndex;
|
||||||
|
this.settings.save();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user