mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-02 16:08:31 +00:00
4.14.0
* Added: Profiles can now be toggled on and off, rather than relying on rules to control them. * Added: Right-clicking the FFZ menu button in the top right of the page will open a Profiles dialog, letting you quickly toggle a profile on or off. * Added: Profiles can now be enabled or disabled based upon the time of day. * Added: Polish and Serbian pluralization rules. * Fixed: Ensure tool-tips work on the new dashboard. * Fixed: Ensure the FFZ Control Center works on the new dashboard.
This commit is contained in:
parent
02efd61f00
commit
62bb6440f3
30 changed files with 503 additions and 52 deletions
|
@ -561,6 +561,12 @@
|
||||||
"css": "youtube-play",
|
"css": "youtube-play",
|
||||||
"code": 61802,
|
"code": 61802,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "861ab06e455e2de3232ebef67d60d708",
|
||||||
|
"css": "minus",
|
||||||
|
"code": 59445,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.13.3",
|
"version": "4.14.0",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
Binary file not shown.
|
@ -112,6 +112,8 @@
|
||||||
|
|
||||||
<glyph glyph-name="clip" unicode="" d="M900 608l-14 99a50 50 0 0 1-57 43l-123-18 95-138 99 14z m-251-35l-96 138-148-21 95-138 149 21z m-287-42l-95 138-124-17a50 50 0 0 1-42-57l14-99 247 35z m538-131v-404c0-28-22-46-50-46h-700c-28 0-50 18-50 46v404h250l-100-150h150l100 150h150l-100-150h150l100 150h100z" horiz-adv-x="1000" />
|
<glyph glyph-name="clip" unicode="" d="M900 608l-14 99a50 50 0 0 1-57 43l-123-18 95-138 99 14z m-251-35l-96 138-148-21 95-138 149 21z m-287-42l-95 138-124-17a50 50 0 0 1-42-57l14-99 247 35z m538-131v-404c0-28-22-46-50-46h-700c-28 0-50 18-50 46v404h250l-100-150h150l100 150h150l-100-150h150l100 150h100z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="minus" unicode="" d="M786 439v-107q0-22-16-38t-38-15h-678q-23 0-38 15t-16 38v107q0 23 16 38t38 16h678q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
||||||
|
|
||||||
<glyph glyph-name="link-ext" unicode="" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
<glyph glyph-name="link-ext" unicode="" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="twitter" unicode="" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />
|
<glyph glyph-name="twitter" unicode="" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />
|
||||||
|
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -486,7 +486,7 @@ export class TranslationManager extends Module {
|
||||||
out = 'FFZ Control Center';
|
out = 'FFZ Control Center';
|
||||||
else {
|
else {
|
||||||
let label = match[1];
|
let label = match[1];
|
||||||
if ( label === 'Proxy.render' && location[2].includes('.vue') )
|
if ( (label === 'Proxy.render' || label.startsWith('Proxy.push')) && location[2].includes('.vue') )
|
||||||
label = 'Vue Component';
|
label = 'Vue Component';
|
||||||
|
|
||||||
out = `${label} (${location[2]}:${location[3]})`;
|
out = `${label} (${location[2]}:${location[3]})`;
|
||||||
|
|
|
@ -7,16 +7,24 @@
|
||||||
<template v-if="i !== item">» </template>
|
<template v-if="i !== item">» </template>
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
<section v-if="! context.currentProfile.live && item.profile_warning !== false" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
|
<section v-if="(! context.currentProfile.live || ! context.currentProfile.toggled) && item.profile_warning !== false" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
|
||||||
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1">
|
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1">
|
||||||
<h3 class="ffz-i-attention">
|
<h3 class="ffz-i-attention">
|
||||||
{{ t('setting.profiles.inactive', "This profile isn't active.") }}
|
{{ t('setting.profiles.inactive', "This profile is not active.") }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{{ t('setting.profiles.inactive.description',
|
<span v-if="! context.currentProfile.toggled">
|
||||||
"This profile's rules don't match the current context and it therefore isn't currently active, so you " +
|
{{ t('setting.profiles.disabled.description',
|
||||||
"won't see changes you make here reflected on Twitch.")
|
"This profile has been disabled, so you won't see changes you make here reflected on Twitch.")
|
||||||
}}
|
}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else>
|
||||||
|
{{ t('setting.profiles.inactive.description',
|
||||||
|
"This profile's rules don't match the current context and it therefore isn't currently active, so you " +
|
||||||
|
"won't see changes you make here reflected on Twitch.")
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section v-if="context.has_update" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
|
<section v-if="context.has_update" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
|
||||||
|
|
|
@ -164,16 +164,28 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-border-l tw-mg-l-1 tw-pd-l-1">
|
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-border-l tw-mg-l-1 tw-pd-l-1">
|
||||||
<div v-if="p.live" class="ffz--profile__icon ffz-i-ok tw-relative tw-tooltip-wrapper">
|
<button class="tw-button tw-button--text" @click="toggle(p)">
|
||||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
<div
|
||||||
{{ t('setting.profiles.active', 'This profile is active.') }}
|
:class="{
|
||||||
|
'ffz-i-ok': p.live,
|
||||||
|
'ffz-i-cancel': ! p.toggled,
|
||||||
|
'ffz-i-minus': p.toggled && ! p.live
|
||||||
|
}"
|
||||||
|
class="ffz--profile__icon tw-relative tw-tooltip-wrapper"
|
||||||
|
>
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
<span v-if="p.live">
|
||||||
|
{{ t('setting.profiles.active', 'This profile is enabled and active.') }}
|
||||||
|
</span>
|
||||||
|
<span v-if="! p.toggled">
|
||||||
|
{{ t('setting.profiles.disabled', 'This profile is disabled.') }}
|
||||||
|
</span>
|
||||||
|
<span v-if="p.toggled && ! p.live">
|
||||||
|
{{ t('setting.profiles.disabled.rules', 'This profile is enabled, but inactive due to its rules.') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div v-if="! p.live" class="ffz--profile__icon ffz-i-cancel tw-relative tw-tooltip-wrapper">
|
|
||||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
|
||||||
{{ t('setting.profiles.inactive', 'This profile is not active.') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -247,6 +259,10 @@ export default {
|
||||||
this.$emit('change-item', item);
|
this.$emit('change-item', item);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggle(profile) {
|
||||||
|
profile.toggle();
|
||||||
|
},
|
||||||
|
|
||||||
resetImport() {
|
resetImport() {
|
||||||
this.import_error = false;
|
this.import_error = false;
|
||||||
this.import_error_message = null;
|
this.import_error_message = null;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
@keyup.space="focusShow"
|
@keyup.space="focusShow"
|
||||||
@click="togglePopup"
|
@click="togglePopup"
|
||||||
>
|
>
|
||||||
{{ t(context.currentProfile.i18n_key, context.currentProfile.title, context.currentProfile) }}
|
{{ context.currentProfile.i18n_key ? t(context.currentProfile.i18n_key, context.currentProfile.title, context.currentProfile) : context.currentProfile.title }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="opened"
|
v-if="opened"
|
||||||
|
@ -51,6 +51,14 @@
|
||||||
@keyup.enter="changeProfile(p)"
|
@keyup.enter="changeProfile(p)"
|
||||||
@click="changeProfile(p)"
|
@click="changeProfile(p)"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="! p.toggled"
|
||||||
|
class="tw-tooltip-wrapper ffz--profile-row__icon ffz-i-cancel tw-absolute"
|
||||||
|
>
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.profiles.disabled', 'This profile is disabled.') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="p.live"
|
v-if="p.live"
|
||||||
class="tw-tooltip-wrapper ffz--profile-row__icon ffz-i-ok tw-absolute"
|
class="tw-tooltip-wrapper ffz--profile-row__icon ffz-i-ok tw-absolute"
|
||||||
|
|
|
@ -526,6 +526,8 @@ export default class MainMenu extends Module {
|
||||||
order: context.manager.__profiles.indexOf(profile),
|
order: context.manager.__profiles.indexOf(profile),
|
||||||
live: context.__profiles.includes(profile),
|
live: context.__profiles.includes(profile),
|
||||||
|
|
||||||
|
toggled: profile.toggled,
|
||||||
|
|
||||||
title: profile.name,
|
title: profile.name,
|
||||||
i18n_key: profile.i18n_key,
|
i18n_key: profile.i18n_key,
|
||||||
|
|
||||||
|
@ -541,6 +543,7 @@ export default class MainMenu extends Module {
|
||||||
profile.save()
|
profile.save()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggle: () => profile.toggled = ! profile.toggled,
|
||||||
getBackup: () => deep_copy(profile.getBackup()),
|
getBackup: () => deep_copy(profile.getBackup()),
|
||||||
|
|
||||||
context: deep_copy(profile.context),
|
context: deep_copy(profile.context),
|
||||||
|
@ -617,6 +620,11 @@ export default class MainMenu extends Module {
|
||||||
this._update_profiles(profile);
|
this._update_profiles(profile);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_profile_toggled(profile, val) {
|
||||||
|
Vue.set(profile_keys[profile.id], 'toggled', val);
|
||||||
|
this._update_profiles(profile);
|
||||||
|
},
|
||||||
|
|
||||||
_profile_deleted(profile) {
|
_profile_deleted(profile) {
|
||||||
Vue.delete(profile_keys, profile.id);
|
Vue.delete(profile_keys, profile.id);
|
||||||
this._update_profiles();
|
this._update_profiles();
|
||||||
|
@ -641,6 +649,7 @@ export default class MainMenu extends Module {
|
||||||
_add_user() {
|
_add_user() {
|
||||||
this._users++;
|
this._users++;
|
||||||
if ( this._users === 1 ) {
|
if ( this._users === 1 ) {
|
||||||
|
settings.on(':profile-toggled', this._profile_toggled, this);
|
||||||
settings.on(':profile-created', this._profile_created, this);
|
settings.on(':profile-created', this._profile_created, this);
|
||||||
settings.on(':profile-changed', this._profile_changed, this);
|
settings.on(':profile-changed', this._profile_changed, this);
|
||||||
settings.on(':profile-deleted', this._profile_deleted, this);
|
settings.on(':profile-deleted', this._profile_deleted, this);
|
||||||
|
@ -654,6 +663,7 @@ export default class MainMenu extends Module {
|
||||||
_remove_user() {
|
_remove_user() {
|
||||||
this._users--;
|
this._users--;
|
||||||
if ( this._users === 0 ) {
|
if ( this._users === 0 ) {
|
||||||
|
settings.off(':profile-toggled', this._profile_toggled, this);
|
||||||
settings.off(':profile-created', this._profile_created, this);
|
settings.off(':profile-created', this._profile_created, this);
|
||||||
settings.off(':profile-changed', this._profile_changed, this);
|
settings.off(':profile-changed', this._profile_changed, this);
|
||||||
settings.off(':profile-deleted', this._profile_deleted, this);
|
settings.off(':profile-deleted', this._profile_deleted, this);
|
||||||
|
|
|
@ -60,7 +60,8 @@ export default class TooltipProvider extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
const container = document.querySelector('#root>div') || document.querySelector('#root') || document.querySelector('.clips-root') || document.body;
|
const container = document.querySelector('.sunlight-root') || document.querySelector('#root>.tw-absolute:not(.tw-flex)') || document.querySelector('#root') || document.querySelector('.clips-root') || document.body;
|
||||||
|
|
||||||
// is_minimal = false; //container && container.classList.contains('twilight-minimal-root');
|
// is_minimal = false; //container && container.classList.contains('twilight-minimal-root');
|
||||||
|
|
||||||
this.tips = new Tooltip(container, 'ffz-tooltip', {
|
this.tips = new Tooltip(container, 'ffz-tooltip', {
|
||||||
|
|
47
src/settings/components/time.vue
Normal file
47
src/settings/components/time.vue
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<template>
|
||||||
|
<section class="tw-flex-grow-1 tw-align-self-start tw-flex tw-align-items-center">
|
||||||
|
<div class="tw-flex tw-align-items-center">
|
||||||
|
<div class="tw-mg-r-1">
|
||||||
|
{{ t(type.i18n, type.title) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label :for="'start-time$' + id" class="tw-mg-l-1">
|
||||||
|
{{ t('settings.filter.time.start', 'Start:') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
:id="'start-time$' + id"
|
||||||
|
v-model="value.data[0]"
|
||||||
|
type="time"
|
||||||
|
class="ffz-min-width-unset tw-flex-grow-1 tw-border-radius-medium tw-font-size-6 tw-mg-x-1 tw-pd-x-1 tw-pd-y-05 tw-input"
|
||||||
|
>
|
||||||
|
|
||||||
|
<label :for="'end-time$' + id" class="tw-mg-l-1">
|
||||||
|
{{ t('settings.filter.time.end', 'End:') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
:id="'end-time$' + id"
|
||||||
|
v-model="value.data[1]"
|
||||||
|
type="time"
|
||||||
|
class="ffz-min-width-unset tw-flex-grow-1 tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 tw-mg-x-1 tw-input"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
let last_id = 0;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['value', 'type', 'filters', 'context'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: last_id++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -118,8 +118,9 @@ export default class SettingsContext extends EventEmitter {
|
||||||
selectProfiles() {
|
selectProfiles() {
|
||||||
const new_profiles = [],
|
const new_profiles = [],
|
||||||
order = this.order = [];
|
order = this.order = [];
|
||||||
|
|
||||||
for(const profile of this.manager.__profiles)
|
for(const profile of this.manager.__profiles)
|
||||||
if ( profile.matches(this.__context) ) {
|
if ( profile.toggled && profile.matches(this.__context) ) {
|
||||||
new_profiles.push(profile);
|
new_profiles.push(profile);
|
||||||
order.push(profile.id);
|
order.push(profile.id);
|
||||||
}
|
}
|
||||||
|
@ -389,6 +390,16 @@ export default class SettingsContext extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hasProfile(profile) {
|
||||||
|
if ( typeof profile === 'number' )
|
||||||
|
for(const prof of this.__profiles)
|
||||||
|
if ( prof.id === profile )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return this.__profiles.includes(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_getRaw(key, type) {
|
_getRaw(key, type) {
|
||||||
if ( ! type )
|
if ( ! type )
|
||||||
throw new Error(`non-existent type for ${key}`)
|
throw new Error(`non-existent type for ${key}`)
|
||||||
|
|
|
@ -42,6 +42,74 @@ export const Or = {
|
||||||
|
|
||||||
// Context Stuff
|
// Context Stuff
|
||||||
|
|
||||||
|
function parseTime(time) {
|
||||||
|
if ( typeof time !== 'string' || ! time.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const idx = time.indexOf(':');
|
||||||
|
if ( idx === -1 )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let hours, minutes;
|
||||||
|
try {
|
||||||
|
hours = parseInt(time.slice(0, idx), 10);
|
||||||
|
minutes = parseInt(time.slice(idx + 1), 10);
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hours * 60 + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Time = {
|
||||||
|
_captured: new Set,
|
||||||
|
|
||||||
|
createTest(config) {
|
||||||
|
const start = parseTime(config[0]),
|
||||||
|
end = parseTime(config[1]);
|
||||||
|
|
||||||
|
if ( start == null || end == null )
|
||||||
|
return () => false;
|
||||||
|
|
||||||
|
if ( start <= end )
|
||||||
|
return () => {
|
||||||
|
Time._captured.add(start);
|
||||||
|
Time._captured.add(end + 1);
|
||||||
|
|
||||||
|
const d = new Date,
|
||||||
|
v = d.getHours() * 60 + d.getMinutes();
|
||||||
|
|
||||||
|
return v >= start && v <= end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
Time._captured.add(start + 1);
|
||||||
|
Time._captured.add(end);
|
||||||
|
|
||||||
|
const d = new Date,
|
||||||
|
v = d.getHours() * 60 + d.getMinutes();
|
||||||
|
|
||||||
|
return v <= start || v >= end;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
captured: () => {
|
||||||
|
const out = Array.from(Time._captured);
|
||||||
|
Time._captured = new Set;
|
||||||
|
out.sort((a, b) => a - b);
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
title: 'Time of Day',
|
||||||
|
i18n: 'settings.filter.time',
|
||||||
|
|
||||||
|
default: ['08:00', '18:00'],
|
||||||
|
|
||||||
|
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/time.vue')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const TheaterMode = {
|
export const TheaterMode = {
|
||||||
createTest(config) {
|
createTest(config) {
|
||||||
return ctx => ctx.ui && ctx.ui.theatreModeEnabled === config;
|
return ctx => ctx.ui && ctx.ui.theatreModeEnabled === config;
|
||||||
|
|
|
@ -46,7 +46,6 @@ export default class SettingsManager extends Module {
|
||||||
|
|
||||||
this.migrations = new MigrationManager(this);
|
this.migrations = new MigrationManager(this);
|
||||||
|
|
||||||
|
|
||||||
// Also create the main context as early as possible.
|
// Also create the main context as early as possible.
|
||||||
this.main_context = new SettingsContext(this);
|
this.main_context = new SettingsContext(this);
|
||||||
|
|
||||||
|
@ -98,6 +97,42 @@ export default class SettingsManager extends Module {
|
||||||
this.log.info(`Initialization complete after ${duration.toFixed(5)}ms -- Values: ${this.provider.size} -- Profiles: ${this.__profiles.length}`)
|
this.log.info(`Initialization complete after ${duration.toFixed(5)}ms -- Values: ${this.provider.size} -- Profiles: ${this.__profiles.length}`)
|
||||||
|
|
||||||
this.scheduleUpdates();
|
this.scheduleUpdates();
|
||||||
|
this.updateClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateClock() {
|
||||||
|
const captured = require('./filters').Time.captured();
|
||||||
|
if ( ! captured?.length )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( this._time_timer )
|
||||||
|
clearTimeout(this._time_timer);
|
||||||
|
|
||||||
|
const d = new Date,
|
||||||
|
now = d.getHours() * 60 + d.getMinutes();
|
||||||
|
|
||||||
|
let next = this._time_next != null ? this._time_next : null;
|
||||||
|
for(const value of captured) {
|
||||||
|
if ( value <= now )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( next == null || value < next )
|
||||||
|
next = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no time waiting for today. Skip to the next day.
|
||||||
|
if ( next == null )
|
||||||
|
next = captured[0] + 1440;
|
||||||
|
|
||||||
|
// Determine how long it'll take to reach the next time period.
|
||||||
|
const delta = (next - now) * 60 * 1000 - 59750 + (60000 - Date.now() % 60000);
|
||||||
|
this._time_timer = setTimeout(() => {
|
||||||
|
for(const context of this.__contexts)
|
||||||
|
context.selectProfiles();
|
||||||
|
|
||||||
|
this.updateClock();
|
||||||
|
}, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,6 +221,8 @@ export default class SettingsManager extends Module {
|
||||||
// Look up the profile it belongs to and emit a changed event from
|
// Look up the profile it belongs to and emit a changed event from
|
||||||
// that profile, thus notifying any contexts or UI instances.
|
// that profile, thus notifying any contexts or UI instances.
|
||||||
key = key.substr(2);
|
key = key.substr(2);
|
||||||
|
|
||||||
|
// Is it a value?
|
||||||
const idx = key.indexOf(':');
|
const idx = key.indexOf(':');
|
||||||
if ( idx === -1 )
|
if ( idx === -1 )
|
||||||
return;
|
return;
|
||||||
|
@ -193,8 +230,12 @@ export default class SettingsManager extends Module {
|
||||||
const profile = this.__profile_ids[key.slice(0, idx)],
|
const profile = this.__profile_ids[key.slice(0, idx)],
|
||||||
s_key = key.slice(idx + 1);
|
s_key = key.slice(idx + 1);
|
||||||
|
|
||||||
if ( profile )
|
if ( profile ) {
|
||||||
profile.emit('changed', s_key, new_value, deleted);
|
if ( s_key === ':enabled' )
|
||||||
|
profile.emit('toggled', profile, deleted ? true : new_value);
|
||||||
|
else
|
||||||
|
profile.emit('changed', s_key, new_value, deleted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -210,6 +251,17 @@ export default class SettingsManager extends Module {
|
||||||
// And then re-select the active profiles.
|
// And then re-select the active profiles.
|
||||||
for(const context of this.__contexts)
|
for(const context of this.__contexts)
|
||||||
context.selectProfiles();
|
context.selectProfiles();
|
||||||
|
|
||||||
|
this.updateClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_onProfileToggled(profile, val) {
|
||||||
|
for(const context of this.__contexts)
|
||||||
|
context.selectProfiles();
|
||||||
|
|
||||||
|
this.updateClock();
|
||||||
|
this.emit(':profile-toggled', profile, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -250,6 +302,9 @@ export default class SettingsManager extends Module {
|
||||||
let reordered = false,
|
let reordered = false,
|
||||||
changed = false;
|
changed = false;
|
||||||
|
|
||||||
|
for(const profile of old_profiles)
|
||||||
|
profile.off('toggled', this._onProfileToggled, this);
|
||||||
|
|
||||||
for(const profile_data of raw_profiles) {
|
for(const profile_data of raw_profiles) {
|
||||||
const id = profile_data.id,
|
const id = profile_data.id,
|
||||||
slot_id = profiles.length,
|
slot_id = profiles.length,
|
||||||
|
@ -293,12 +348,17 @@ export default class SettingsManager extends Module {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(const profile of profiles)
|
||||||
|
profile.on('toggled', this._onProfileToggled, this);
|
||||||
|
|
||||||
if ( ! changed && ! old_ids.size || suppress_events )
|
if ( ! changed && ! old_ids.size || suppress_events )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(const context of this.__contexts)
|
for(const context of this.__contexts)
|
||||||
context.selectProfiles();
|
context.selectProfiles();
|
||||||
|
|
||||||
|
this.updateClock();
|
||||||
|
|
||||||
for(const id of new_ids)
|
for(const id of new_ids)
|
||||||
this.emit(':profile-created', profile_ids[id]);
|
this.emit(':profile-created', profile_ids[id]);
|
||||||
|
|
||||||
|
@ -327,9 +387,10 @@ export default class SettingsManager extends Module {
|
||||||
options.name = `Unnamed Profile ${i}`;
|
options.name = `Unnamed Profile ${i}`;
|
||||||
|
|
||||||
const profile = this.__profile_ids[i] = new SettingsProfile(this, options);
|
const profile = this.__profile_ids[i] = new SettingsProfile(this, options);
|
||||||
|
|
||||||
this.__profiles.unshift(profile);
|
this.__profiles.unshift(profile);
|
||||||
|
|
||||||
|
profile.on('toggled', this._onProfileToggled, this);
|
||||||
|
|
||||||
this._saveProfiles();
|
this._saveProfiles();
|
||||||
this.emit(':profile-created', profile);
|
this.emit(':profile-created', profile);
|
||||||
return profile;
|
return profile;
|
||||||
|
@ -351,6 +412,7 @@ export default class SettingsManager extends Module {
|
||||||
if ( profile.id === 0 )
|
if ( profile.id === 0 )
|
||||||
throw new Error('cannot delete default profile');
|
throw new Error('cannot delete default profile');
|
||||||
|
|
||||||
|
profile.off('toggled', this._onProfileToggled, this);
|
||||||
profile.clear();
|
profile.clear();
|
||||||
this.__profile_ids[id] = null;
|
this.__profile_ids[id] = null;
|
||||||
|
|
||||||
|
@ -400,6 +462,8 @@ export default class SettingsManager extends Module {
|
||||||
this.provider.set('profiles', this.__profiles.map(prof => prof.data));
|
this.provider.set('profiles', this.__profiles.map(prof => prof.data));
|
||||||
for(const context of this.__contexts)
|
for(const context of this.__contexts)
|
||||||
context.selectProfiles();
|
context.selectProfiles();
|
||||||
|
|
||||||
|
this.updateClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.prefix = `p:${this.id}:`;
|
this.prefix = `p:${this.id}:`;
|
||||||
|
this.enabled_key = `${this.prefix}:enabled`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get data() {
|
get data() {
|
||||||
|
@ -38,6 +39,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
desc_i18n_key: this.desc_i18n_key,
|
desc_i18n_key: this.desc_i18n_key,
|
||||||
|
|
||||||
url: this.url,
|
url: this.url,
|
||||||
|
show_toggle: this.show_toggle,
|
||||||
|
|
||||||
context: this.context
|
context: this.context
|
||||||
}
|
}
|
||||||
|
@ -72,6 +74,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
version: 2,
|
version: 2,
|
||||||
type: 'profile',
|
type: 'profile',
|
||||||
profile: this.data,
|
profile: this.data,
|
||||||
|
toggled: this.toggled,
|
||||||
values: {}
|
values: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,6 +110,23 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Toggled
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
get toggled() {
|
||||||
|
return this.provider.get(this.enabled_key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
set toggled(val) {
|
||||||
|
if ( val === this.toggleState )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.provider.set(this.enabled_key, val);
|
||||||
|
this.emit('toggled', this, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Context
|
// Context
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
@ -158,7 +178,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
len = p.length;
|
len = p.length;
|
||||||
|
|
||||||
for(const key of this.provider.keys())
|
for(const key of this.provider.keys())
|
||||||
if ( key.startsWith(p) )
|
if ( key.startsWith(p) && key !== this.enabled_key )
|
||||||
out.push(key.slice(len));
|
out.push(key.slice(len));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -168,7 +188,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
const p = this.prefix,
|
const p = this.prefix,
|
||||||
len = p.length;
|
len = p.length;
|
||||||
for(const key of this.provider.keys())
|
for(const key of this.provider.keys())
|
||||||
if ( key.startsWith(p) ) {
|
if ( key.startsWith(p) && key !== this.enabled_key ) {
|
||||||
this.provider.delete(key);
|
this.provider.delete(key);
|
||||||
this.emit('changed', key.slice(len), undefined, true);
|
this.emit('changed', key.slice(len), undefined, true);
|
||||||
}
|
}
|
||||||
|
@ -179,7 +199,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
len = p.length;
|
len = p.length;
|
||||||
|
|
||||||
for(const key of this.provider.keys())
|
for(const key of this.provider.keys())
|
||||||
if ( key.startsWith(p) )
|
if ( key.startsWith(p) && key !== this.enabled_key )
|
||||||
yield [key.slice(len), this.provider.get(key)];
|
yield [key.slice(len), this.provider.get(key)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +208,7 @@ export default class SettingsProfile extends EventEmitter {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for(const key of this.provider.keys())
|
for(const key of this.provider.keys())
|
||||||
if ( key.startsWith(p) )
|
if ( key.startsWith(p) && key !== this.enabled_key )
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
|
|
@ -236,6 +236,6 @@ Twilight.ROUTES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Twilight.DIALOG_EXCLUSIVE = '.twilight-main,.twilight-minimal-root>div,#root>div>.tw-full-height,.clips-root';
|
Twilight.DIALOG_EXCLUSIVE = '.sunlight-root,.twilight-main,.twilight-minimal-root>div,#root>div>.tw-full-height,.clips-root';
|
||||||
Twilight.DIALOG_MAXIMIZED = '.twilight-main,.twilight-minimal-root,#root .dashboard-side-nav+.tw-full-height,.clips-root>.tw-full-height .scrollable-area';
|
Twilight.DIALOG_MAXIMIZED = '.sunlight-page,.twilight-main,.twilight-minimal-root,#root .dashboard-side-nav+.tw-full-height,.clips-root>.tw-full-height .scrollable-area';
|
||||||
Twilight.DIALOG_SELECTOR = '#root>div>.tw-full-height,.twilight-minimal-root>.tw-full-height,.clips-root>.tw-full-height .scrollable-area';
|
Twilight.DIALOG_SELECTOR = '.sunlight-root,#root,.twilight-minimal-root>.tw-full-height,.clips-root>.tw-full-height .scrollable-area';
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import {DEBUG} from 'utilities/constants';
|
import {DEBUG} from 'utilities/constants';
|
||||||
import {SiteModule} from 'utilities/module';
|
import {SiteModule} from 'utilities/module';
|
||||||
import {createElement} from 'utilities/dom';
|
import {createElement, ClickOutside, setChildren} from 'utilities/dom';
|
||||||
|
|
||||||
export default class MenuButton extends SiteModule {
|
export default class MenuButton extends SiteModule {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
|
@ -192,6 +192,7 @@ export default class MenuButton extends SiteModule {
|
||||||
{btn = (<button
|
{btn = (<button
|
||||||
class="tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon tw-core-button tw-core-button--border tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative"
|
class="tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon tw-core-button tw-core-button--border tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative"
|
||||||
onClick={e => this.handleClick(e, btn)} // eslint-disable-line react/jsx-no-bind
|
onClick={e => this.handleClick(e, btn)} // eslint-disable-line react/jsx-no-bind
|
||||||
|
onContextMenu={e => this.renderContext(e, btn)} // eslint-disable-line react/jsx-no-bind
|
||||||
>
|
>
|
||||||
<div class="tw-align-items-center tw-flex tw-flex-grow-0">
|
<div class="tw-align-items-center tw-flex tw-flex-grow-0">
|
||||||
<span class="tw-button-icon__icon">
|
<span class="tw-button-icon__icon">
|
||||||
|
@ -256,6 +257,9 @@ export default class MenuButton extends SiteModule {
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
container.insertBefore(el, container.lastElementChild);
|
container.insertBefore(el, container.lastElementChild);
|
||||||
|
|
||||||
|
if ( this._ctx_open )
|
||||||
|
this.renderContext(null, btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(event, btn) {
|
handleClick(event, btn) {
|
||||||
|
@ -269,13 +273,160 @@ export default class MenuButton extends SiteModule {
|
||||||
this.emit(':clicked', event, btn);
|
this.emit(':clicked', event, btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderButtonIcon(profile) {
|
||||||
|
const live = this.settings.main_context.hasProfile(profile);
|
||||||
|
return (<figure class={`ffz--profile__icon ${live ? 'ffz-i-ok' : profile.toggled ? 'ffz-i-minus' : 'ffz-i-cancel'}`} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButtonTip(profile) {
|
||||||
|
if ( ! profile.toggled )
|
||||||
|
return this.i18n.t('setting.profiles.disabled', 'This profile is disabled.');
|
||||||
|
|
||||||
|
if ( this.settings.main_context.hasProfile(profile) )
|
||||||
|
return this.i18n.t('setting.profiles.active', 'This profile is enabled and active.');
|
||||||
|
|
||||||
|
return this.i18n.t('setting.profiles.disabled.rules', 'This profile is enabled, but inactive due to its rules.');
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContext(event, btn) {
|
||||||
|
if ( event ) {
|
||||||
|
if ( event.shiftKey || event.ctrlKey )
|
||||||
|
return;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = btn.parentElement.parentElement;
|
||||||
|
let ctx = container.querySelector('.ffz--menu-context');
|
||||||
|
if ( ctx ) {
|
||||||
|
if ( ctx._ffz_destroy )
|
||||||
|
ctx._ffz_destroy();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const destroy = () => {
|
||||||
|
this._ctx_open = false;
|
||||||
|
|
||||||
|
if ( ctx._ffz_outside )
|
||||||
|
ctx._ffz_outside.destroy();
|
||||||
|
|
||||||
|
ctx.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const profiles = [];
|
||||||
|
|
||||||
|
for(const profile of this.settings.__profiles) {
|
||||||
|
const toggle = (<button
|
||||||
|
class="tw-flex-shrink-0 tw-mg-r-1 tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-button-icon--secondary tw-core-button tw-core-button--border tw-inline-flex tw-interactive tw-justify-content-center tw-relative ffz-tooltip ffz-tooltip--no-mouse"
|
||||||
|
data-title={this.renderButtonTip(profile)}
|
||||||
|
onClick={e => { // eslint-disable-line react/jsx-no-bind
|
||||||
|
profile.toggled = ! profile.toggled;
|
||||||
|
|
||||||
|
setChildren(toggle, this.renderButtonIcon(profile));
|
||||||
|
toggle.dataset.title = this.renderButtonTip(profile);
|
||||||
|
|
||||||
|
if ( toggle['_ffz_tooltip$0']?.rerender )
|
||||||
|
toggle['_ffz_tooltip$0'].rerender();
|
||||||
|
|
||||||
|
this.emit('tooltips:cleanup');
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ this.renderButtonIcon(profile) }
|
||||||
|
</button>)
|
||||||
|
|
||||||
|
profiles.push(<div class="tw-relative tw-border-b tw-pd-y-05 tw-pd-l-1 tw-flex">
|
||||||
|
{toggle}
|
||||||
|
<div>
|
||||||
|
<h4>{ profile.i18n_key ? this.i18n.t(profile.i18n_key, profile.name, profile) : profile.name }</h4>
|
||||||
|
{profile.description && (<div class="description">
|
||||||
|
{ profile.desc_i18n_key ? this.i18n.t(profile.desc_i18n_key, profile.description, profile) : profile.description }
|
||||||
|
</div>)}
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ctx = (<div class="tw-absolute tw-balloon tw-balloon--down tw-balloon--lg tw-balloon--right tw-block ffz--menu-context">
|
||||||
|
<div class="tw-border-radius-large tw-c-background-base tw-c-text-inherit tw-elevation-4">
|
||||||
|
<div class="tw-c-text-base tw-elevation-1 tw-flex tw-flex-shrink-0 tw-pd-x-1 tw-pd-y-05 tw-popover-header">
|
||||||
|
<div class="tw-flex tw-flex-column tw-justify-content-center tw-mg-l-05 tw-popover-header__icon-slot--left" />
|
||||||
|
<div class="tw-align-items-center tw-flex tw-flex-column tw-flex-grow-1 tw-justify-content-center">
|
||||||
|
<h5 class="tw-align-center tw-c-text-alt tw-semibold">
|
||||||
|
{ this.i18n.t('site.menu_button.profiles', 'Profiles') }
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex tw-flex-column tw-justify-content-center tw-mg-l-05 tw-popover-header__icon-slot--right">
|
||||||
|
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
||||||
|
<button
|
||||||
|
class="tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-button-icon--secondary tw-core-button tw-core-button--border tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden"
|
||||||
|
onClick={e => this.openSettings(e)} // eslint-disable-line react/jsx-no-bind
|
||||||
|
>
|
||||||
|
<span class="tw-button-icon__icon">
|
||||||
|
<figure class="ffz-i-cog" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-center">
|
||||||
|
{ this.i18n.t('setting.profiles.configure', 'Configure') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex tw-flex-column tw-justify-content-center tw-mg-l-05 tw-popover-header__icon-slot--right">
|
||||||
|
<button
|
||||||
|
class="tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-button-icon--secondary tw-core-button tw-core-button--border tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative"
|
||||||
|
onClick={destroy} // eslint-disable-line react/jsx-no-bind
|
||||||
|
>
|
||||||
|
<span class="tw-button-icon__icon">
|
||||||
|
<figure class="ffz-i-cancel" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="center-window__long-scrollable-area scrollable-area scrollable-area--suppress-scroll-x" data-simplebar>
|
||||||
|
{profiles}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
|
||||||
|
ctx._ffz_destroy = destroy;
|
||||||
|
ctx._ffz_outside = new ClickOutside(ctx, destroy);
|
||||||
|
container.appendChild(ctx);
|
||||||
|
|
||||||
|
this._ctx_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
openSettings() {
|
||||||
|
const menu = this.resolve('main_menu');
|
||||||
|
if ( ! menu )
|
||||||
|
return;
|
||||||
|
|
||||||
|
menu.requestPage('data_management.profiles');
|
||||||
|
if ( menu.showing )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.emit(':clicked');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadMenu(event, btn, page) {
|
||||||
|
const menu = this.resolve('main_menu');
|
||||||
|
if ( ! menu )
|
||||||
|
return;
|
||||||
|
|
||||||
loadMenu(event, btn) {
|
|
||||||
const cl = btn && btn.classList;
|
const cl = btn && btn.classList;
|
||||||
if ( cl )
|
if ( cl )
|
||||||
cl.add('loading');
|
cl.add('loading');
|
||||||
|
|
||||||
this.resolve('main_menu').enable(event).then(() => {
|
if ( page )
|
||||||
|
menu.requestPage(page);
|
||||||
|
if ( menu.showing )
|
||||||
|
return;
|
||||||
|
|
||||||
|
menu.enable(event).then(() => {
|
||||||
if ( cl )
|
if ( cl )
|
||||||
cl.remove('loading');
|
cl.remove('loading');
|
||||||
|
|
||||||
|
|
|
@ -80,5 +80,6 @@ export default [
|
||||||
"sort-alt-down",
|
"sort-alt-down",
|
||||||
"user",
|
"user",
|
||||||
"clip",
|
"clip",
|
||||||
"youtube-play"
|
"youtube-play",
|
||||||
|
"minus"
|
||||||
];
|
];
|
|
@ -487,6 +487,8 @@ const CARDINAL_TO_LANG = {
|
||||||
german: ['de', 'el', 'en', 'es', 'fi', 'hu', 'it', 'nl', 'no', 'nb', 'tr', 'sv'],
|
german: ['de', 'el', 'en', 'es', 'fi', 'hu', 'it', 'nl', 'no', 'nb', 'tr', 'sv'],
|
||||||
hebrew: ['he'],
|
hebrew: ['he'],
|
||||||
persian: ['fa'],
|
persian: ['fa'],
|
||||||
|
polish: ['pl'],
|
||||||
|
serbian: ['sr'],
|
||||||
french: ['fr', 'pt'],
|
french: ['fr', 'pt'],
|
||||||
russian: ['ru','uk']
|
russian: ['ru','uk']
|
||||||
}
|
}
|
||||||
|
@ -522,6 +524,28 @@ const CARDINAL_TYPES = {
|
||||||
|
|
||||||
persian: (n, i) => (i === 0 || n === 1) ? 1 : 5,
|
persian: (n, i) => (i === 0 || n === 1) ? 1 : 5,
|
||||||
|
|
||||||
|
serbian(n, i, v, t) {
|
||||||
|
if ( v !== 0 ) return 5;
|
||||||
|
const i1 = i % 10, i2 = i % 100;
|
||||||
|
const t1 = t % 10, t2 = t % 100;
|
||||||
|
if ( i1 === 1 && i2 !== 11 ) return 1;
|
||||||
|
if ( t1 === 1 && t2 !== 11 ) return 1;
|
||||||
|
if ( i1 >= 2 && i1 <= 4 && !(i2 >= 12 && i2 <= 14) ) return 3;
|
||||||
|
if ( t1 >= 2 && t1 <= 4 && !(t2 >= 12 && t2 <= 14) ) return 3;
|
||||||
|
return 5;
|
||||||
|
},
|
||||||
|
|
||||||
|
polish(n, i, v) {
|
||||||
|
if ( v !== 0 ) return 5;
|
||||||
|
if ( n === 1 ) return 1;
|
||||||
|
const n1 = n % 10, n2 = n % 100;
|
||||||
|
if ( n1 >= 2 && n1 <= 4 && !(n2 >= 12 && n2 <= 14) ) return 3;
|
||||||
|
if ( i !== 1 && (n1 === 0 || n1 === 1) ) return 4;
|
||||||
|
if ( n1 >= 5 && n1 <= 9 ) return 4;
|
||||||
|
if ( n2 >= 12 && n2 <= 14 ) return 4;
|
||||||
|
return 5;
|
||||||
|
},
|
||||||
|
|
||||||
russian(n,i,v) {
|
russian(n,i,v) {
|
||||||
const n1 = n % 10, n2 = n % 100;
|
const n1 = n % 10, n2 = n % 100;
|
||||||
if ( n1 === 1 && n2 !== 11 ) return 1;
|
if ( n1 === 1 && n2 !== 11 ) return 1;
|
||||||
|
|
|
@ -61,7 +61,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-has-dialog {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.ffz-has-dialog > :not(.ffz-dialog) {
|
& > :not(.ffz-dialog) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -52,6 +52,7 @@
|
||||||
.ffz-i-play:before { content: '\e832'; } /* '' */
|
.ffz-i-play:before { content: '\e832'; } /* '' */
|
||||||
.ffz-i-user:before { content: '\e833'; } /* '' */
|
.ffz-i-user:before { content: '\e833'; } /* '' */
|
||||||
.ffz-i-clip:before { content: '\e834'; } /* '' */
|
.ffz-i-clip:before { content: '\e834'; } /* '' */
|
||||||
|
.ffz-i-minus:before { content: '\e835'; } /* '' */
|
||||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||||
.ffz-i-github:before { content: '\f09b'; } /* '' */
|
.ffz-i-github:before { content: '\f09b'; } /* '' */
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -52,6 +52,7 @@
|
||||||
.ffz-i-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-user { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-user { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-clip { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-clip { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.ffz-i-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-github { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-github { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
.ffz-i-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-user { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-user { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-clip { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-clip { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.ffz-i-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.ffz-i-github { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.ffz-i-github { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ffz-fontello';
|
font-family: 'ffz-fontello';
|
||||||
src: url('../font/ffz-fontello.eot?84652283');
|
src: url('../font/ffz-fontello.eot?15513462');
|
||||||
src: url('../font/ffz-fontello.eot?84652283#iefix') format('embedded-opentype'),
|
src: url('../font/ffz-fontello.eot?15513462#iefix') format('embedded-opentype'),
|
||||||
url('../font/ffz-fontello.woff2?84652283') format('woff2'),
|
url('../font/ffz-fontello.woff2?15513462') format('woff2'),
|
||||||
url('../font/ffz-fontello.woff?84652283') format('woff'),
|
url('../font/ffz-fontello.woff?15513462') format('woff'),
|
||||||
url('../font/ffz-fontello.ttf?84652283') format('truetype'),
|
url('../font/ffz-fontello.ttf?15513462') format('truetype'),
|
||||||
url('../font/ffz-fontello.svg?84652283#ffz-fontello') format('svg');
|
url('../font/ffz-fontello.svg?15513462#ffz-fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ffz-fontello';
|
font-family: 'ffz-fontello';
|
||||||
src: url('../font/ffz-fontello.svg?84652283#ffz-fontello') format('svg');
|
src: url('../font/ffz-fontello.svg?15513462#ffz-fontello') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -108,6 +108,7 @@
|
||||||
.ffz-i-play:before { content: '\e832'; } /* '' */
|
.ffz-i-play:before { content: '\e832'; } /* '' */
|
||||||
.ffz-i-user:before { content: '\e833'; } /* '' */
|
.ffz-i-user:before { content: '\e833'; } /* '' */
|
||||||
.ffz-i-clip:before { content: '\e834'; } /* '' */
|
.ffz-i-clip:before { content: '\e834'; } /* '' */
|
||||||
|
.ffz-i-minus:before { content: '\e835'; } /* '' */
|
||||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||||
.ffz-i-github:before { content: '\f09b'; } /* '' */
|
.ffz-i-github:before { content: '\f09b'; } /* '' */
|
||||||
|
|
|
@ -149,12 +149,18 @@ textarea.tw-input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz--profile-row__icon,
|
||||||
.ffz--profile__icon {
|
.ffz--profile__icon {
|
||||||
&.ffz-i-ok,
|
&.ffz-i-ok,
|
||||||
.ffz-i-ok {
|
.ffz-i-ok {
|
||||||
color: var(--color-green-darker);
|
color: var(--color-green-darker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ffz-i-minus,
|
||||||
|
.ffz-i-minus {
|
||||||
|
color: var(--color-text-alt-2);
|
||||||
|
}
|
||||||
|
|
||||||
&.ffz-i-cancel,
|
&.ffz-i-cancel,
|
||||||
.ffz-i-cancel {
|
.ffz-i-cancel {
|
||||||
color: var(--color-red);
|
color: var(--color-red);
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
border-left: 4px solid;
|
border-left: 4px solid;
|
||||||
border-left-color: transparent;
|
border-left-color: transparent;
|
||||||
|
|
||||||
&.live .ffz--profile-row__icon {
|
/*&.live .ffz--profile-row__icon {
|
||||||
color: var(--color-green-darker);
|
color: var(--color-green-darker);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
&:not(.live):not(:hover):not(:focus) {
|
&:not(.live):not(:hover):not(:focus) {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue