1
- import { Plugin } from 'obsidian' ;
2
- import { nanoid } from 'nanoid' ;
3
-
4
- type GistJSON = {
5
- description : string ,
6
- public : Boolean ,
7
- created_at : string ,
8
- files : [ string ] ,
9
- owner : string ,
10
- div : string ,
11
- stylesheet : string
12
- }
13
-
14
- const pluginName = "obsidian-gist"
15
- const obsidianAppOrigin = 'app://obsidian.md'
16
-
17
- export default class GistPlugin extends Plugin {
18
- async onload ( ) {
19
- this . _injectContainerHeightAdjustmentScript ( )
20
-
21
- this . registerMarkdownCodeBlockProcessor ( "gist" , async ( sourceString : string , el , ctx ) => {
22
- const gists = sourceString . trim ( ) . split ( "\n" )
23
-
24
- return Promise . all (
25
- gists . map ( async ( gist ) => {
26
- return this . _processGist ( el , gist )
27
- } )
28
- )
29
- } ) ;
30
- }
31
-
32
- // private
33
-
34
- async _processGist ( el : HTMLElement , gistString : string ) {
35
- const pattern = / (?< protocol > h t t p s ? : \/ \/ ) ? (?< host > g i s t \. g i t h u b \. c o m \/ ) ? ( (?< username > \w + ) \/ ) ? (?< gistID > \w + ) ( \# (?< filename > .+ ) ) ? /
36
-
37
- const matchResult = gistString . match ( pattern ) . groups
38
-
39
- const gistID = matchResult . gistID
40
-
41
- if ( gistID === undefined ) {
42
- return this . _showError ( el , gistString , `Could not found a valid Gist ID, please make sure your content and format is correct.` )
43
- }
44
-
45
- let gistURL = `https://gist.github.com/${ gistID } .json`
46
-
47
- if ( matchResult . filename !== undefined ) {
48
- gistURL = `${ gistURL } ?file=${ matchResult . filename } `
49
- }
50
-
51
- try {
52
- const response = await fetch ( gistURL )
53
-
54
- if ( response . ok ) {
55
- const gistJSON = await response . json ( ) as GistJSON
56
- return this . _insertGistElement ( el , gistID , gistJSON )
57
- } else {
58
- return this . _showError ( el , gistString , `Could not fetch the Gist info from GitHub server. (Code: ${ response . status } )` )
59
- }
60
- } catch ( error ) {
61
- return this . _showError ( el , gistString , `Could not fetch the Gist from GitHub server. (Error: ${ error } )` )
62
- }
63
- }
64
-
65
- async _insertGistElement ( el : HTMLElement , gistID : string , gistJSON : GistJSON ) {
66
- // generate an uuid for each gist element
67
- const gistUUID = `${ pluginName } -${ gistID } -${ nanoid ( ) } `
68
-
69
- // container
70
- const container = document . createElement ( 'iframe' ) ;
71
- container . id = gistUUID
72
- container . classList . add ( `${ pluginName } -container` )
73
- container . setAttribute ( 'sandbox' , 'allow-scripts allow-top-navigation-by-user-activation' )
74
- container . setAttribute ( 'loading' , 'lazy' )
75
-
76
- // reset the default things on HTML
77
- const resetStylesheet = `
78
- <style>
79
- html, body {
80
- margin: 0;
81
- padding: 0;
82
- height: 100%;
83
- }
84
- </style>
85
- `
86
-
87
- // height adjustment script
88
- const heightAdjustmentScript = `
89
- <script>
90
- deliverHeightMessage = () => {
91
- const contentHeight = document.body.scrollHeight;
92
-
93
- top.postMessage({
94
- sender: '${ pluginName } ',
95
- gistUUID: '${ gistUUID } ',
96
- contentHeight: contentHeight
97
- }, '${ obsidianAppOrigin } ');
98
- }
99
-
100
- window.addEventListener('load', () => {
101
- deliverHeightMessage();
102
- })
103
- </script>
104
- `
105
-
106
- // build stylesheet link
107
- const stylesheetLink = document . createElement ( 'link' ) ;
108
- stylesheetLink . rel = "stylesheet" ;
109
- stylesheetLink . href = gistJSON . stylesheet
110
-
111
- // hack to make links open in the parent
112
- const parentLinkHack = document . createElement ( 'base' )
113
- parentLinkHack . target = "_parent"
114
-
115
- // Inject content into the iframe
116
- container . srcdoc = `
117
- <html>
118
- <head>
119
- <!-- hack -->
120
- ${ resetStylesheet }
121
- ${ parentLinkHack . outerHTML }
122
- ${ heightAdjustmentScript }
123
-
124
- <!-- gist style -->
125
- ${ stylesheetLink . outerHTML }
126
- </head>
127
-
128
- <body>
129
- ${ gistJSON . div }
130
- </body>
131
- </html>
132
- `
133
-
134
- // insert container into the DOM
135
- el . appendChild ( container )
136
- }
137
-
138
- async _showError ( el : HTMLElement , gistIDAndFilename : String , errorMessage : String = '' ) {
139
- const errorText = `
140
- Failed to load the Gist (${ gistIDAndFilename } ).
141
-
142
- Error:
143
-
144
- ${ errorMessage }
145
- ` . trim ( )
146
-
147
- el . createEl ( 'pre' , { text : errorText } )
148
- }
149
-
150
- _injectContainerHeightAdjustmentScript ( ) {
151
- const containerHeightAdjustmentScript = document . createElement ( 'script' )
152
- containerHeightAdjustmentScript . id = `${ pluginName } -container-height-adjustment`
153
- containerHeightAdjustmentScript . textContent = `
154
- window.addEventListener("message", (messageEvent) => {
155
- const sender = messageEvent.data.sender
156
-
157
- if (messageEvent.origin !== 'null') {
158
- // a message received from the iFrame with \`srcdoc\` attribute, the \`origin\` will be \`null\`.
159
- return;
160
- }
161
-
162
- // only process message coming from this plugin
163
- if (sender === '${ pluginName } ') {
164
- const gistUUID = messageEvent.data.gistUUID
165
- const contentHeight = messageEvent.data.contentHeight
166
-
167
- const gistContainer = document.querySelector('iframe#' + gistUUID)
168
- gistContainer.height = contentHeight
169
- }
170
- }, false)
171
- `
172
-
173
- document . head . appendChild ( containerHeightAdjustmentScript )
174
- }
175
- }
1
+ import { nanoid } from 'nanoid' ;
2
+
3
+ type GistJSON = {
4
+ description : string ,
5
+ public : Boolean ,
6
+ created_at : string ,
7
+ files : [ string ] ,
8
+ owner : string ,
9
+ div : string ,
10
+ stylesheet : string
11
+ }
12
+
13
+ const pluginName = "obsidian-gist"
14
+ const obsidianAppOrigin = 'app://obsidian.md'
15
+
16
+ class GistProcessor {
17
+ constructor ( ) {
18
+ }
19
+
20
+ injectContainerHeightAdjustmentScript = ( ) => {
21
+ const containerHeightAdjustmentScript = document . createElement ( 'script' )
22
+ containerHeightAdjustmentScript . id = `${ pluginName } -container-height-adjustment`
23
+ containerHeightAdjustmentScript . textContent = `
24
+ window.addEventListener("message", (messageEvent) => {
25
+ const sender = messageEvent.data.sender
26
+
27
+ if (messageEvent.origin !== 'null') {
28
+ // a message received from the iFrame with \`srcdoc\` attribute, the \`origin\` will be \`null\`.
29
+ return;
30
+ }
31
+
32
+ // only process message coming from this plugin
33
+ if (sender === '${ pluginName } ') {
34
+ const gistUUID = messageEvent.data.gistUUID
35
+ const contentHeight = messageEvent.data.contentHeight
36
+
37
+ const gistContainer = document.querySelector('iframe#' + gistUUID)
38
+ gistContainer.height = contentHeight
39
+ }
40
+ }, false)
41
+ `
42
+
43
+ document . head . appendChild ( containerHeightAdjustmentScript )
44
+ }
45
+
46
+ processor = async ( sourceString : string , el : HTMLElement ) => {
47
+ const gists = sourceString . trim ( ) . split ( "\n" )
48
+
49
+ return Promise . all (
50
+ gists . map ( async ( gist ) => {
51
+ return this . _processGist ( el , gist )
52
+ } )
53
+ )
54
+ } ;
55
+
56
+ // private
57
+
58
+ async _processGist ( el : HTMLElement , gistString : string ) {
59
+ const pattern = / (?< protocol > h t t p s ? : \/ \/ ) ? (?< host > g i s t \. g i t h u b \. c o m \/ ) ? ( (?< username > \w + ) \/ ) ? (?< gistID > \w + ) ( \# (?< filename > .+ ) ) ? /
60
+
61
+ const matchResult = gistString . match ( pattern ) . groups
62
+
63
+ const gistID = matchResult . gistID
64
+
65
+ if ( gistID === undefined ) {
66
+ return this . _showError ( el , gistString , `Could not found a valid Gist ID, please make sure your content and format is correct.` )
67
+ }
68
+
69
+ let gistURL = `https://gist.github.com/${ gistID } .json`
70
+
71
+ if ( matchResult . filename !== undefined ) {
72
+ gistURL = `${ gistURL } ?file=${ matchResult . filename } `
73
+ }
74
+
75
+ try {
76
+ const response = await fetch ( gistURL )
77
+
78
+ if ( response . ok ) {
79
+ const gistJSON = await response . json ( ) as GistJSON
80
+ return this . _insertGistElement ( el , gistID , gistJSON )
81
+ } else {
82
+ return this . _showError ( el , gistString , `Could not fetch the Gist info from GitHub server. (Code: ${ response . status } )` )
83
+ }
84
+ } catch ( error ) {
85
+ return this . _showError ( el , gistString , `Could not fetch the Gist from GitHub server. (Error: ${ error } )` )
86
+ }
87
+ }
88
+
89
+ async _insertGistElement ( el : HTMLElement , gistID : string , gistJSON : GistJSON ) {
90
+ // generate an uuid for each gist element
91
+ const gistUUID = `${ pluginName } -${ gistID } -${ nanoid ( ) } `
92
+
93
+ // container
94
+ const container = document . createElement ( 'iframe' ) ;
95
+ container . id = gistUUID
96
+ container . classList . add ( `${ pluginName } -container` )
97
+ container . setAttribute ( 'sandbox' , 'allow-scripts allow-top-navigation-by-user-activation' )
98
+ container . setAttribute ( 'loading' , 'lazy' )
99
+
100
+ // reset the default things on HTML
101
+ const resetStylesheet = `
102
+ <style>
103
+ html, body {
104
+ margin: 0;
105
+ padding: 0;
106
+ height: 100%;
107
+ }
108
+ </style>
109
+ `
110
+
111
+ // height adjustment script
112
+ const heightAdjustmentScript = `
113
+ <script>
114
+ deliverHeightMessage = () => {
115
+ const contentHeight = document.body.scrollHeight;
116
+
117
+ top.postMessage({
118
+ sender: '${ pluginName } ',
119
+ gistUUID: '${ gistUUID } ',
120
+ contentHeight: contentHeight
121
+ }, '${ obsidianAppOrigin } ');
122
+ }
123
+
124
+ window.addEventListener('load', () => {
125
+ deliverHeightMessage();
126
+ })
127
+ </script>
128
+ `
129
+
130
+ // build stylesheet link
131
+ const stylesheetLink = document . createElement ( 'link' ) ;
132
+ stylesheetLink . rel = "stylesheet" ;
133
+ stylesheetLink . href = gistJSON . stylesheet
134
+
135
+ // hack to make links open in the parent
136
+ const parentLinkHack = document . createElement ( 'base' )
137
+ parentLinkHack . target = "_parent"
138
+
139
+ // Inject content into the iframe
140
+ container . srcdoc = `
141
+ <html>
142
+ <head>
143
+ <!-- hack -->
144
+ ${ resetStylesheet }
145
+ ${ parentLinkHack . outerHTML }
146
+ ${ heightAdjustmentScript }
147
+
148
+ <!-- gist style -->
149
+ ${ stylesheetLink . outerHTML }
150
+ </head>
151
+
152
+ <body>
153
+ ${ gistJSON . div }
154
+ </body>
155
+ </html>
156
+ `
157
+
158
+ // insert container into the DOM
159
+ el . appendChild ( container )
160
+ }
161
+
162
+ async _showError ( el : HTMLElement , gistIDAndFilename : String , errorMessage : String = '' ) {
163
+ const errorText = `
164
+ Failed to load the Gist (${ gistIDAndFilename } ).
165
+
166
+ Error:
167
+
168
+ ${ errorMessage }
169
+ ` . trim ( )
170
+
171
+ el . createEl ( 'pre' , { text : errorText } )
172
+ }
173
+
174
+
175
+ }
176
+
177
+ export { GistProcessor }
0 commit comments