1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00
* Added: Support for OpenDyslexic and Google Fonts. Font Family settings now provide a list of fonts to choose from, in addition to allowing a custom font to be entered.
* Changed: When saving a settings backup, the current date will be included in the filename by default. (Closes #1084)
This commit is contained in:
SirStendec 2021-09-06 16:48:48 -04:00
parent a529e7fd27
commit 04cfbe6ed9
23 changed files with 394 additions and 28 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "frankerfacez", "name": "frankerfacez",
"author": "Dan Salvato LLC", "author": "Dan Salvato LLC",
"version": "4.28.7", "version": "4.29.0",
"description": "FrankerFaceZ is a Twitch enhancement suite.", "description": "FrankerFaceZ is a Twitch enhancement suite.",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -200,7 +200,7 @@ export default {
attrs: { attrs: {
'data-title': this.t( 'data-title': this.t(
'tooltip.link-unsafe', 'tooltip.link-unsafe',
"Caution: This URL is on Google's Safe Browsing List for: {reasons}", 'Caution: This URL is has been flagged as potentially harmful by: {reasons}',
{ {
reasons: reasons.toLowerCase() reasons: reasons.toLowerCase()
} }

View file

@ -22,6 +22,7 @@ import * as TOKENIZERS from './tokenizers';
import * as RICH_PROVIDERS from './rich_providers'; import * as RICH_PROVIDERS from './rich_providers';
import Actions from './actions'; import Actions from './actions';
import { getFontsList } from 'src/utilities/fonts';
export const SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]'; export const SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]';
@ -178,7 +179,8 @@ export default class Chat extends Module {
path: 'Chat > Appearance >> General', path: 'Chat > Appearance >> General',
title: 'Font Family', title: 'Font Family',
description: 'Set the font used for displaying chat messages.', description: 'Set the font used for displaying chat messages.',
component: 'setting-text-box' component: 'setting-combo-box',
data: () => getFontsList()
} }
}); });

View file

@ -138,7 +138,7 @@ export const Links = {
url_notice = (<div class="ffz-i-attention"> url_notice = (<div class="ffz-i-attention">
{this.i18n.tList( {this.i18n.tList(
'tooltip.link-unsafe', 'tooltip.link-unsafe',
"Caution: This URL is on Google's Safe Browsing List for: {reasons}", 'Caution: This URL is has been flagged as potentially harmful by: {reasons}',
{reasons: reasons.toLowerCase()} {reasons: reasons.toLowerCase()}
)} )}
</div>); </div>);

View file

@ -16,13 +16,31 @@
class="tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-font-size-6 ffz-select tw-pd-l-1 tw-pd-r-3 tw-pd-y-05" class="tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-font-size-6 ffz-select tw-pd-l-1 tw-pd-r-3 tw-pd-y-05"
@change="onChange" @change="onChange"
> >
<option <template v-for="i in nested_data">
v-for="i in data" <optgroup
:key="i.value" v-if="i.entries"
:selected="i.value === value" :key="i.key"
> :disabled="i.disabled"
{{ i.i18n_key ? t(i.i18n_key, i.title, i) : i.title }} :label="i.i18n_key ? t(i.i18n_key, i.title, i) : i.title"
</option> >
<option
v-for="j in i.entries"
:key="j.value"
:selected="j.value === value"
:value="j.v"
>
{{ j.i18n_key ? t(j.i18n_key, j.title, j) : j.title }}
</option>
</optgroup>
<option
v-else
:key="i.value"
:selected="i.value === value"
:value="i.v"
>
{{ i.i18n_key ? t(i.i18n_key, i.title, i) : i.title }}
</option>
</template>
<option :selected="isCustom"> <option :selected="isCustom">
{{ t('setting.combo-box.custom', 'Custom') }} {{ t('setting.combo-box.custom', 'Custom') }}
</option> </option>
@ -91,6 +109,36 @@ export default {
} }
}, },
computed: {
nested_data() {
const out = [];
let current_group = null;
let i = 0;
for(const entry of this.data) {
if ( entry.separator ) {
current_group = {
key: entry.key ?? i,
entries: [],
i18n_key: entry.i18n_key,
title: entry.title,
disabled: entry.disabled
};
out.push(current_group);
} else if ( current_group != null )
current_group.entries.push(Object.assign({v: i}, entry));
else
out.push(Object.assign({v: i}, entry));
i++;
}
return out;
}
},
watch: { watch: {
value(val) { value(val) {
for(const item of this.data) for(const item of this.data)
@ -108,7 +156,7 @@ export default {
methods: { methods: {
onChange() { onChange() {
const idx = this.$refs.control.selectedIndex, const idx = this.$refs.control.value,
raw_value = this.data[idx]; raw_value = this.data[idx];
if ( raw_value ) { if ( raw_value ) {

View file

@ -388,13 +388,16 @@ export default class SettingsManager extends Module {
// ======================================================================== // ========================================================================
async generateBackupFile() { async generateBackupFile() {
const now = new Date(),
timestamp = `${now.getFullYear()}-${now.getMonth()}-${now.getDate()}`;
if ( await this._needsZipBackup() ) { if ( await this._needsZipBackup() ) {
const blob = await this._getZipBackup(); const blob = await this._getZipBackup();
return new File([blob], 'ffz-settings.zip', {type: 'application/zip'}); return new File([blob], `ffz-settings (${timestamp}).zip`, {type: 'application/zip'});
} }
const settings = await this.getSettingsDump(); const settings = await this.getSettingsDump();
return new File([JSON.stringify(settings)], 'ffz-settings.json', {type: 'application/json;charset=utf-8'}); return new File([JSON.stringify(settings)], `ffz-settings (${timestamp}).json`, {type: 'application/json;charset=utf-8'});
} }

View file

@ -6,6 +6,7 @@
import {get} from 'utilities/object'; import {get} from 'utilities/object';
import {ColorAdjuster} from 'utilities/color'; import {ColorAdjuster} from 'utilities/color';
import { useFont } from 'utilities/fonts';
import Module from 'utilities/module'; import Module from 'utilities/module';
@ -88,6 +89,14 @@ export default class Chat extends Module {
lh = Math.round((20/12) * size); lh = Math.round((20/12) * size);
let font = this.chat.context.get('chat.font-family') || 'inherit'; let font = this.chat.context.get('chat.font-family') || 'inherit';
const [processed, unloader] = useFont(font);
font = processed;
if ( this._font_unloader )
this._font_unloader();
this._font_unloader = unloader;
if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 ) if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 )
font = `"${font}"`; font = `"${font}"`;

View file

@ -8,7 +8,8 @@ import Module from 'utilities/module';
import {createElement, on, off} from 'utilities/dom'; import {createElement, on, off} from 'utilities/dom';
import {isValidShortcut, debounce, has} from 'utilities/object'; import {isValidShortcut, debounce, has} from 'utilities/object';
import { IS_FIREFOX } from 'src/utilities/constants'; import { IS_FIREFOX } from 'utilities/constants';
import { getFontsList, useFont } from 'utilities/fonts';
const STYLE_VALIDATOR = createElement('span'); const STYLE_VALIDATOR = createElement('span');
@ -417,7 +418,8 @@ export default class PlayerBase extends Module {
path: 'Player > Closed Captioning >> Font', path: 'Player > Closed Captioning >> Font',
title: 'Font Family', title: 'Font Family',
description: 'Override the font used for displaying Closed Captions.', description: 'Override the font used for displaying Closed Captions.',
component: 'setting-text-box' component: 'setting-combo-box',
data: () => getFontsList()
}, },
changed: () => this.updateCaptionsCSS() changed: () => this.updateCaptionsCSS()
}); });
@ -1031,20 +1033,33 @@ export default class PlayerBase extends Module {
updateCaptionsCSS() { updateCaptionsCSS() {
// Font // Font
const font_out = [];
const font_size = this.settings.get('player.captions.font-size'); const font_size = this.settings.get('player.captions.font-size');
let font_family = this.settings.get('player.captions.font-family'); let font_family = this.settings.get('player.captions.font-family');
if ( font_family.indexOf(' ') !== -1 && font_family.indexOf(',') === -1 && font_family.indexOf('"') === -1 && font_family.indexOf("'") === -1 )
font_family = `"${font_family}"`; if ( font_family && font_family.length ) {
const [processed, unloader] = useFont(font_family);
font_family = processed;
if ( this._font_unloader )
this._font_unloader();
this._font_unloader = unloader;
if ( font_family.indexOf(' ') !== -1 && font_family.indexOf(',') === -1 && font_family.indexOf('"') === -1 && font_family.indexOf("'") === -1 )
font_family = `"${font_family}"`;
STYLE_VALIDATOR.style.fontFamily = '';
STYLE_VALIDATOR.style.fontFamily = font_family;
if ( STYLE_VALIDATOR.style.fontFamily )
font_out.push(`font-family: ${STYLE_VALIDATOR.style.fontFamily} !important;`);
}
STYLE_VALIDATOR.style.fontSize = ''; STYLE_VALIDATOR.style.fontSize = '';
STYLE_VALIDATOR.style.fontFamily = '';
STYLE_VALIDATOR.style.fontSize = font_size; STYLE_VALIDATOR.style.fontSize = font_size;
STYLE_VALIDATOR.style.fontFamily = font_family;
const font_out = [];
if ( STYLE_VALIDATOR.style.fontFamily )
font_out.push(`font-family: ${STYLE_VALIDATOR.style.fontFamily} !important;`);
if ( STYLE_VALIDATOR.style.fontSize ) if ( STYLE_VALIDATOR.style.fontSize )
font_out.push(`font-size: ${STYLE_VALIDATOR.style.fontSize} !important;`); font_out.push(`font-size: ${STYLE_VALIDATOR.style.fontSize} !important;`);

View file

@ -8,6 +8,7 @@ import {Color, ColorAdjuster} from 'utilities/color';
import {get, has, make_enum, shallow_object_equals, set_equals, deep_equals} from 'utilities/object'; import {get, has, make_enum, shallow_object_equals, set_equals, deep_equals} from 'utilities/object';
import {WEBKIT_CSS as WEBKIT} from 'utilities/constants'; import {WEBKIT_CSS as WEBKIT} from 'utilities/constants';
import {FFZEvent} from 'utilities/events'; import {FFZEvent} from 'utilities/events';
import {useFont} from 'utilities/fonts';
import Module from 'utilities/module'; import Module from 'utilities/module';
@ -717,6 +718,14 @@ export default class ChatHook extends Module {
lh = Math.round((20/12) * size); lh = Math.round((20/12) * size);
let font = this.chat.context.get('chat.font-family') || 'inherit'; let font = this.chat.context.get('chat.font-family') || 'inherit';
const [processed, unloader] = useFont(font);
font = processed;
if ( this._font_unloader )
this._font_unloader();
this._font_unloader = unloader;
if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 ) if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 )
font = `"${font}"`; font = `"${font}"`;

View file

@ -169,7 +169,7 @@ export default class RichContent extends Module {
return (<div return (<div
class="ffz--corner-flag ffz--corner-flag__warn ffz-tooltip ffz-tooltip--no-mouse" class="ffz--corner-flag ffz--corner-flag__warn ffz-tooltip ffz-tooltip--no-mouse"
data-title={t.i18n.t('tooltip.link-unsafe', "Caution: This URL is on Google's Safe Browsing List for: {reasons}", {reasons})} data-title={t.i18n.t('tooltip.link-unsafe', 'Caution: This URL is has been flagged as potentially harmful by: {reasons}', {reasons})}
> >
<figure class="ffz-i-attention" /> <figure class="ffz-i-attention" />
</div>); </div>);

View file

@ -7,6 +7,7 @@
import Module from 'utilities/module'; import Module from 'utilities/module';
import {ManagedStyle} from 'utilities/dom'; import {ManagedStyle} from 'utilities/dom';
import {has} from 'utilities/object'; import {has} from 'utilities/object';
import { getFontsList, useFont } from 'utilities/fonts';
const STYLE_VALIDATOR = document.createElement('span'); const STYLE_VALIDATOR = document.createElement('span');
@ -378,8 +379,9 @@ export default class CSSTweaks extends Module {
ui: { ui: {
path: 'Appearance > Theme >> Fonts', path: 'Appearance > Theme >> Fonts',
title: 'Font Family', title: 'Font Family',
description: 'Override the font used for the entire Twitch website. The old default font was: `"Helvetica Neue",Helvetica,Arial,sans-serif`', description: 'Override the font used for the entire Twitch website. The old default font was: `"Helvetica Neue",Helvetica,Arial,sans-serif`\n\nAny font available via [Google Fonts](https://fonts.google.com/) can be loaded by prefixing the font name with `google:`.',
component: 'setting-text-box' component: 'setting-combo-box',
data: () => getFontsList()
}, },
changed: () => this.updateFont() changed: () => this.updateFont()
}); });
@ -481,6 +483,14 @@ export default class CSSTweaks extends Module {
updateFont() { updateFont() {
let font = this.settings.get('layout.theme.global-font'); let font = this.settings.get('layout.theme.global-font');
if ( font && font.length ) { if ( font && font.length ) {
const [processed, unloader] = useFont(font);
font = processed;
if ( this._font_unloader )
this._font_unloader();
this._font_unloader = unloader;
if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 ) if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 )
font = `"${font}"`; font = `"${font}"`;

210
src/utilities/fonts.js Normal file
View file

@ -0,0 +1,210 @@
import { createElement } from "./dom";
const KNOWN_FONTS = [
'Roobert', // Twitch Default
'Arial',
'Arial Black',
'Verdana',
'Helvetica',
'Tahoma',
'Trebuchet MS',
'Impact',
'Didot',
'American Typewriter',
'Lucida Console',
'Monaco',
'Bradley Hand',
'Times New Roman',
'Georgia',
'Garamond',
'Courier New',
'Brush Script MT',
'Comic Sans MS',
];
export const VALID_FONTS = KNOWN_FONTS.filter(font => document.fonts.check(`16px ${font}`)).sort();
/* Google Font Handling */
const GOOGLE_FONTS = [
'Roboto',
'Open Sans',
'Noto Sans JP',
'Lato',
'Montserrat',
'Roboto Condensed',
'Source Sans Pro',
'Oswald',
'Poppins',
'Noto Sans',
'Roboto Mono',
'Raleway',
'Ubuntu',
'Merriweather',
'Nunito',
'PT Sans',
'Roboto Slab',
'Playfair Display',
'Lora',
'Rubik',
'Mukta',
'Noto Sans KR',
'Work Sans',
'Nunito Sans',
'Nanum Gothic',
'Inter',
'Quicksand',
'PT Serif',
'Hind Siliguri',
'Titilium Web',
'Fira Sans',
'Noto Serif',
'Noto Sans TC',
'Karla'
];
const LOADED_GOOGLE = new Map();
const LOADED_GOOGLE_LINKS = new Map();
function loadGoogleFont(font) {
if ( LOADED_GOOGLE_LINKS.has(font) )
return;
const name = encodeURIComponent(font);
const link = createElement('link', {
id: `ffz-font-${name}`,
rel: 'stylesheet',
href: `https://fonts.googleapis.com/css2?family=${name}`
});
LOADED_GOOGLE_LINKS.set(font, link);
document.head.appendChild(link);
}
function unloadGoogleFont(font) {
const link = LOADED_GOOGLE_LINKS.get(font);
if ( ! link )
return;
LOADED_GOOGLE_LINKS.delete(font);
link.remove();
}
/* OpenDyslexic Font */
const OD_FONTS = [
'OpenDyslexic',
'OpenDyslexicAlta',
'OpenDyslexicMono'
];
import OD_URL from 'styles/opendyslexic.scss';
let od_count = 0;
let od_link = null;
function loadOpenDyslexic() {
if ( od_link )
return;
od_link = createElement('link', {
id: `ffz-font-opendyslexic`,
rel: 'stylesheet',
type: 'text/css',
href: OD_URL
});
document.head.appendChild(od_link);
}
function unloadOpenDyslexic() {
if ( ! od_link )
return;
od_link.remove();
od_link = null;
}
/* Using and Listing Fonts */
export function getFontsList() {
const out = [
{value: '', i18n_key: 'setting.font.default', title: 'Default'},
{separator: true, i18n_key: 'setting.font.builtin', title: 'Built-in Fonts'},
];
for(const font of VALID_FONTS)
out.push({value: font, title: font});
out.push({
separator: true, i18n_key: 'setting.font.dyslexic', title: 'Dyslexia Fonts'
});
for(const font of OD_FONTS)
out.push({value: font, title: font});
out.push({
separator: true, i18n_key: 'setting.font.google', title: 'Google Fonts'
});
for(const font of GOOGLE_FONTS)
out.push({value: `google:${font}`, title: font});
return out;
}
export function useFont(font) {
if ( ! font )
return [font, null];
if ( OD_FONTS.includes(font) ) {
od_count++;
if ( od_count === 1 )
loadOpenDyslexic();
let unloaded = false;
const unloader = () => {
if ( ! unloaded ) {
unloaded = true;
od_count--;
if ( ! od_count )
unloadOpenDyslexic();
}
}
return [font, unloader];
}
if ( font.startsWith('google:') ) {
const name = font.slice(7),
count = (LOADED_GOOGLE.get(name) ?? 0) + 1;
LOADED_GOOGLE.set(name, count);
if ( count === 1 )
loadGoogleFont(name);
let unloaded = false;
const unloader = () => {
if ( ! unloaded ) {
unloaded = true;
const count = (LOADED_GOOGLE.get(name) ?? 0) - 1;
if ( count > 0 ) {
LOADED_GOOGLE.set(name, count);
} else {
LOADED_GOOGLE.delete(name);
unloadGoogleFont(name);
}
}
}
return [name, unloader];
}
return [font, null];
}

60
styles/opendyslexic.scss Normal file
View file

@ -0,0 +1,60 @@
@font-face {
font-family: 'opendyslexic';
src: url('~res/font/OpenDyslexic-Regular.otf');
font-style: normal;
font-weight: normal;
}
@font-face {
font-family: 'opendyslexic';
src: url('~res/font/OpenDyslexic-Italic.otf');
font-style: italic;
font-weight: normal;
}
@font-face {
font-family: 'opendyslexic';
src: url('~res/font/OpenDyslexic-Bold.otf');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'opendyslexic';
src: url('~res/font/OpenDyslexic-BoldItalic.otf');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'opendyslexicmono';
src: url('~res/font/OpenDyslexicMono-Regular.otf');
}
@font-face {
font-family: 'opendyslexicalta';
src: url('~res/font/OpenDyslexicAlta-Regular.otf');
font-style: normal;
font-weight: normal;
}
@font-face {
font-family: 'opendyslexicalta';
src: url('~res/font/OpenDyslexicAlta-Italic.otf');
font-style: italic;
font-weight: normal;
}
@font-face {
font-family: 'opendyslexicalta';
src: url('~res/font/OpenDyslexicAlta-Bold.otf');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'opendyslexicalta';
src: url('~res/font/OpenDyslexicAlta-BoldItalic.otf');
font-weight: bold;
font-style: italic;
}

View file

@ -119,7 +119,7 @@ module.exports = {
loader: 'graphql-tag/loader' loader: 'graphql-tag/loader'
}, },
{ {
test: /\.(?:eot|ttf|woff|woff2)$/, test: /\.(?:otf|eot|ttf|woff|woff2)$/,
use: [{ use: [{
loader: 'file-loader', loader: 'file-loader',
options: { options: {