1+ /*
2+ * Copyright 2010-2020 Gildas Lormeau
3+ * contact : gildas.lormeau <at> gmail.com
4+ *
5+ * This file is part of SingleFile.
6+ *
7+ * The code in this file is free software: you can redistribute it and/or
8+ * modify it under the terms of the GNU Affero General Public License
9+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
10+ * of the License, or (at your option) any later version.
11+ *
12+ * The code in this file is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
15+ * General Public License for more details.
16+ *
17+ * As additional permission under GNU AGPL version 3 section 7, you may
18+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
19+ * AGPL normally required by section 4, provided you include this license
20+ * notice and a URL through which recipients can access the Corresponding
21+ * Source.
22+ */
23+
24+ /* global globalThis, window */
25+
26+ const document = globalThis . document ;
27+ const Document = globalThis . Document ;
28+
29+ if ( document instanceof Document ) {
30+ const scriptElement = document . createElement ( "script" ) ;
31+ scriptElement . src = "data:," + "(" + injectedScript . toString ( ) + ")()" ;
32+ ( document . documentElement || document ) . appendChild ( scriptElement ) ;
33+ scriptElement . remove ( ) ;
34+ }
35+
36+ function injectedScript ( ) {
37+ if ( typeof globalThis == "undefined" ) {
38+ window . globalThis = window ;
39+ }
40+ const document = globalThis . document ;
41+ const console = globalThis . console ;
42+ const dispatchEvent = event => globalThis . dispatchEvent ( event ) ;
43+ const CustomEvent = globalThis . CustomEvent ;
44+ const FileReader = globalThis . FileReader ;
45+ const Blob = globalThis . Blob ;
46+ const warn = ( console && console . warn && ( ( ...args ) => console . warn ( ...args ) ) ) || ( ( ) => { } ) ;
47+ const NEW_FONT_FACE_EVENT = "single-filez-new-font-face" ;
48+ const DELETE_FONT_EVENT = "single-filez-delete-font" ;
49+ const CLEAR_FONTS_EVENT = "single-filez-clear-fonts" ;
50+ const FONT_STYLE_PROPERTIES = {
51+ family : "font-family" ,
52+ style : "font-style" ,
53+ weight : "font-weight" ,
54+ stretch : "font-stretch" ,
55+ unicodeRange : "unicode-range" ,
56+ variant : "font-variant" ,
57+ featureSettings : "font-feature-settings"
58+ } ;
59+
60+ if ( globalThis . FontFace ) {
61+ const FontFace = globalThis . FontFace ;
62+ let warningFontFaceDisplayed ;
63+ globalThis . FontFace = function ( ) {
64+ if ( ! warningFontFaceDisplayed ) {
65+ warn ( "SingleFileZ is hooking the FontFace constructor, document.fonts.delete and document.fonts.clear to handle dynamically loaded fonts." ) ;
66+ warningFontFaceDisplayed = true ;
67+ }
68+ getDetailObject ( ...arguments ) . then ( detail => dispatchEvent ( new CustomEvent ( NEW_FONT_FACE_EVENT , { detail } ) ) ) ;
69+ return new FontFace ( ...arguments ) ;
70+ } ;
71+ globalThis . FontFace . toString = function ( ) { return "function FontFace() { [native code] }" ; } ;
72+ const deleteFont = document . fonts . delete ;
73+ document . fonts . delete = function ( fontFace ) {
74+ getDetailObject ( fontFace . family ) . then ( detail => dispatchEvent ( new CustomEvent ( DELETE_FONT_EVENT , { detail } ) ) ) ;
75+ return deleteFont . call ( document . fonts , fontFace ) ;
76+ } ;
77+ document . fonts . delete . toString = function ( ) { return "function delete() { [native code] }" ; } ;
78+ const clearFonts = document . fonts . clear ;
79+ document . fonts . clear = function ( ) {
80+ dispatchEvent ( new CustomEvent ( CLEAR_FONTS_EVENT ) ) ;
81+ return clearFonts . call ( document . fonts ) ;
82+ } ;
83+ document . fonts . clear . toString = function ( ) { return "function clear() { [native code] }" ; } ;
84+ }
85+
86+ async function getDetailObject ( fontFamily , src , descriptors ) {
87+ const detail = { } ;
88+ detail [ "font-family" ] = fontFamily ;
89+ detail . src = src ;
90+ if ( descriptors ) {
91+ Object . keys ( descriptors ) . forEach ( descriptor => {
92+ if ( FONT_STYLE_PROPERTIES [ descriptor ] ) {
93+ detail [ FONT_STYLE_PROPERTIES [ descriptor ] ] = descriptors [ descriptor ] ;
94+ }
95+ } ) ;
96+ }
97+ return new Promise ( resolve => {
98+ if ( detail . src instanceof ArrayBuffer ) {
99+ const reader = new FileReader ( ) ;
100+ reader . readAsDataURL ( new Blob ( [ detail . src ] ) ) ;
101+ reader . addEventListener ( "load" , ( ) => {
102+ detail . src = "url(" + reader . result + ")" ;
103+ resolve ( detail ) ;
104+ } ) ;
105+ } else {
106+ resolve ( detail ) ;
107+ }
108+ } ) ;
109+ }
110+ }
0 commit comments