zenn-embed-elements 0.1.108 → 0.1.109-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,10 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const gist_1 = require("./classes/gist");
4
- const tweet_1 = require("./classes/tweet");
5
3
  const katex_1 = require("./classes/katex");
6
- const mermaid_1 = require("./classes/mermaid");
7
- customElements.define('embed-gist', gist_1.EmbedGist);
8
- customElements.define('embed-tweet', tweet_1.EmbedTweet);
9
4
  customElements.define('embed-katex', katex_1.EmbedKatex);
10
- customElements.define('embed-mermaid', mermaid_1.EmbedMermaid);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zenn-embed-elements",
3
- "version": "0.1.108",
3
+ "version": "0.1.109-alpha.2",
4
4
  "license": "MIT",
5
5
  "description": "Web components for embedded contents.",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  "rimraf": "^3.0.2",
29
29
  "typescript": "^3.9.3"
30
30
  },
31
- "gitHead": "65bea2106504e978987d16ec2f6d024b13314049",
31
+ "gitHead": "951af5e0a49c5c8014d8233e74d6f45ec2f21413",
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  }
@@ -1,12 +0,0 @@
1
- declare type GistApiResponse = {
2
- stylesheet: string;
3
- div: string;
4
- };
5
- export declare class EmbedGist extends HTMLElement {
6
- constructor();
7
- render(data: GistApiResponse): void;
8
- renderError(): void;
9
- getCacheKey(): string;
10
- connectedCallback(): Promise<void>;
11
- }
12
- export {};
@@ -1,73 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.EmbedGist = void 0;
13
- const jsonp_1 = require("../utils/jsonp");
14
- // ホットリロード等、再レンダリング時のちらつきを防ぐためにhtmlの値をキャッシュする(リロードで消える)
15
- const resultHtmlStore = {};
16
- class EmbedGist extends HTMLElement {
17
- constructor() {
18
- super();
19
- const shadowRoot = this.attachShadow({ mode: 'open' });
20
- const cachedHtml = resultHtmlStore[this.getCacheKey()];
21
- if (cachedHtml) {
22
- shadowRoot.innerHTML = cachedHtml;
23
- }
24
- }
25
- render(data) {
26
- if (!(this.shadowRoot && data.stylesheet && data.div)) {
27
- this.renderError();
28
- return;
29
- }
30
- const resultHtml = `<link rel="stylesheet" href="${data.stylesheet}"><div>${data.div}</div>`;
31
- // gistのhtmlをキャッシュする
32
- resultHtmlStore[this.getCacheKey()] = resultHtml;
33
- this.shadowRoot.innerHTML = resultHtml;
34
- }
35
- renderError() {
36
- this.innerHTML = `<div style="text-align: center; margin: 1.5rem 0; color: gray; font-size: 0.9rem;">
37
- Gistの読み込みに失敗しました<br>
38
- ${this.getAttribute('page-url')}
39
- </div>`;
40
- }
41
- getCacheKey() {
42
- return encodeURIComponent(`${this.getAttribute('page-url')}-${this.getAttribute('encoded-filename')}`);
43
- }
44
- connectedCallback() {
45
- return __awaiter(this, void 0, void 0, function* () {
46
- // キャッシュがある場合は再リクエストしない
47
- if (resultHtmlStore[this.getCacheKey()])
48
- return;
49
- const pageUrl = this.getAttribute('page-url');
50
- const encodedFileName = this.getAttribute('encoded-filename');
51
- if (!pageUrl)
52
- return;
53
- const requestURL = pageUrlToRequestUrl(pageUrl) +
54
- ((encodedFileName === null || encodedFileName === void 0 ? void 0 : encodedFileName.length) ? `?file=${encodedFileName}` : '');
55
- try {
56
- const data = yield jsonp_1.getByJsonp(requestURL);
57
- this.render(data);
58
- }
59
- catch (e) {
60
- console.log(e);
61
- this.renderError();
62
- }
63
- });
64
- }
65
- }
66
- exports.EmbedGist = EmbedGist;
67
- function pageUrlToRequestUrl(url) {
68
- if (url.endsWith('.json'))
69
- return url;
70
- if (url.endsWith('.js'))
71
- return url.replace('.js', '.json');
72
- return `${url}.json`;
73
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * original: https://github.com/gitlabhq/gitlabhq/blob/master/app/assets/javascripts/behaviors/markdown/render_mermaid.js
3
- */
4
- export declare class EmbedMermaid extends HTMLElement {
5
- private observer?;
6
- private _tmpContainer?;
7
- connectedCallback(): Promise<void>;
8
- disconnectedCallback(): void;
9
- render(): Promise<void>;
10
- }
@@ -1,174 +0,0 @@
1
- "use strict";
2
- /**
3
- * original: https://github.com/gitlabhq/gitlabhq/blob/master/app/assets/javascripts/behaviors/markdown/render_mermaid.js
4
- */
5
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
- return new (P || (P = Promise))(function (resolve, reject) {
8
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
- step((generator = generator.apply(thisArg, _arguments || [])).next());
12
- });
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.EmbedMermaid = void 0;
16
- const load_script_1 = require("../utils/load-script");
17
- // mermaid.jsのバージョン
18
- const MERMAID_VERSION = '8.13';
19
- // レンダリングする図ごとの最大文字数
20
- const MAX_CHAR_LIMIT = 2000;
21
- // https://mermaid-js.github.io/mermaid/#/flowchart?id=chaining-of-links
22
- // 新しい仕様で
23
- // graph LR
24
- // a --> b & c--> d
25
- // に対応するが、少ない記述でノード接続が爆発する可能性があるため最大数を制限する
26
- const MAX_CHAINING_OF_LINKS_LIMIT = 10;
27
- const containerId = 'mermaid-container';
28
- function initMermaid() {
29
- return __awaiter(this, void 0, void 0, function* () {
30
- if (typeof mermaid === 'undefined') {
31
- yield load_script_1.loadScript({
32
- src: `https://cdn.jsdelivr.net/npm/mermaid@${MERMAID_VERSION}/dist/mermaid.min.js`,
33
- id: 'mermaid-js',
34
- });
35
- const theme = 'default';
36
- // mermaid 本体がロード時に走らないように設定
37
- // mermaid 本体は使わないのでほかは設定しない
38
- // eslint-disable-next-line
39
- mermaid.initialize({
40
- mermaid: {
41
- startOnLoad: false,
42
- },
43
- });
44
- // mermaidAPI の設定
45
- // eslint-disable-next-line
46
- mermaid.mermaidAPI.initialize({
47
- startOnLoad: false,
48
- securityLevel: 'strict',
49
- theme,
50
- er: {
51
- useMaxWidth: true,
52
- },
53
- flowchart: {
54
- useMaxWidth: true,
55
- htmlLabels: false,
56
- },
57
- sequence: {
58
- useMaxWidth: true,
59
- },
60
- class: {
61
- useMaxWidth: true,
62
- },
63
- journey: {
64
- useMaxWidth: true,
65
- },
66
- });
67
- }
68
- });
69
- }
70
- function getPotentialPerformanceRisk(source) {
71
- const cool = (() => {
72
- try {
73
- // eslint-disable-next-line
74
- mermaid.mermaidAPI.parse(source);
75
- return true;
76
- }
77
- catch (e) {
78
- console.log('mermaid.js のレンダリングでシンタックスエラーが発生しました', e);
79
- return false;
80
- }
81
- })();
82
- return {
83
- syntaxError: {
84
- yes: !cool,
85
- message: `<li>シンタックスエラーです</li>`,
86
- },
87
- charLimitOver: {
88
- yes: source.length > MAX_CHAR_LIMIT,
89
- message: `<li>ブロックあたりの文字数上限は${MAX_CHAR_LIMIT}です</li>`,
90
- },
91
- chainingOfLinksOver: {
92
- yes: (source.match(/&/g) || []).length > MAX_CHAINING_OF_LINKS_LIMIT,
93
- message: `<li>ブロックあたりの<code>&</code>によるチェイン上限は${MAX_CHAINING_OF_LINKS_LIMIT}です</li>`,
94
- },
95
- };
96
- }
97
- class EmbedMermaid extends HTMLElement {
98
- connectedCallback() {
99
- return __awaiter(this, void 0, void 0, function* () {
100
- // 一時コンテナ
101
- const tmpContainer = document.createElement('div');
102
- this.appendChild(tmpContainer);
103
- this._tmpContainer = tmpContainer;
104
- // 以下の理由からmermaidのレンダリングを遅延する
105
- // - パフォーマンスのため
106
- // - Safariでdetailsタグ内のmermaid.jsがうまくレンダリングされないため
107
- this.observer = new IntersectionObserver((entries) => {
108
- entries.forEach((entry) => {
109
- var _a;
110
- if (entry.isIntersecting) {
111
- this.render();
112
- if (!this._tmpContainer) {
113
- console.error('Something wrong with _tmpContainer');
114
- return;
115
- }
116
- (_a = this.observer) === null || _a === void 0 ? void 0 : _a.unobserve(this._tmpContainer); // 一度レンダリングされたらobserveを解除する
117
- }
118
- });
119
- }, { rootMargin: '1000px 0px' } // 手前で発火
120
- );
121
- this._tmpContainer && this.observer.observe(this._tmpContainer);
122
- });
123
- }
124
- disconnectedCallback() {
125
- // observeを解除
126
- if (this.observer) {
127
- this._tmpContainer && this.observer.unobserve(this._tmpContainer);
128
- this.observer.disconnect();
129
- }
130
- }
131
- render() {
132
- return __awaiter(this, void 0, void 0, function* () {
133
- yield initMermaid();
134
- const content = this.textContent || this.innerText;
135
- // Mermaid モジュールの読み込みに失敗したり、レンダリング対象のコンテンツが空の場合は何もせずに終了
136
- if (!content)
137
- return;
138
- // 図だけを表示するためにコードは非表示に
139
- const sourceContainer = this.childNodes[0];
140
- sourceContainer.setAttribute('style', 'display:none');
141
- // 文法エラーやパフォーマンスリスクが検出された場合、注意書きをレンダリングして終了
142
- const risk = getPotentialPerformanceRisk(content);
143
- if (Object.values(risk)
144
- .map((r) => r.yes)
145
- .includes(true)) {
146
- this.innerHTML = `
147
- <p>
148
- <span>mermaidをレンダリングできません。</span>
149
- <ul>
150
- ${risk.syntaxError.yes ? risk.syntaxError.message : ''}
151
- ${risk.charLimitOver.yes ? risk.charLimitOver.message : ''}
152
- ${risk.chainingOfLinksOver.yes ? risk.chainingOfLinksOver.message : ''}
153
- </ul>
154
- </p>
155
- `;
156
- return;
157
- }
158
- // すべて通過した場合はレンダリングする
159
- // セキュリティリスクを考慮して bindFunctions は実行しない方針にする
160
- // 今回は `securityLevel='strict'` にしているのでどのみち実行されない
161
- // securityLevel='loose'にし、かつ `Interaction` を有効にする場合は
162
- // https://github.com/mermaidjs/mermaid-gitbook/blob/master/content/usage.md#binding-events
163
- // ここを参考に追加する
164
- const insert = (svgCode) => {
165
- // 描画後のSVGを格納する div タグを作成
166
- const container = document.createElement('div');
167
- this.appendChild(container);
168
- container.innerHTML = svgCode;
169
- };
170
- mermaid === null || mermaid === void 0 ? void 0 : mermaid.mermaidAPI.render(`${containerId}-${Date.now().valueOf()}-render`, content, insert, this._tmpContainer);
171
- });
172
- }
173
- }
174
- exports.EmbedMermaid = EmbedMermaid;
@@ -1,8 +0,0 @@
1
- export declare class EmbedTweet extends HTMLElement {
2
- url?: string;
3
- tweetId?: string;
4
- constructor();
5
- connectedCallback(): Promise<void>;
6
- render(): void;
7
- embedTweet(): Promise<void>;
8
- }
@@ -1,73 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.EmbedTweet = void 0;
13
- /**
14
- * iframeの高さを取ってキャッシュすることで、ホットリロードでのガタツキを防ぐ
15
- */
16
- const heightStore = {};
17
- const containerClassName = 'embed-tweet-container';
18
- const fallbackLinkClassName = 'embed-tweet-link';
19
- class EmbedTweet extends HTMLElement {
20
- constructor() {
21
- super();
22
- const url = this.getAttribute('src');
23
- if (!url)
24
- return;
25
- this.url = url;
26
- const match = url.match(/https?:\/\/twitter.com\/(.*?)\/status\/(\d+)[/?]?/);
27
- if (match && match[2]) {
28
- this.tweetId = match[2];
29
- }
30
- }
31
- connectedCallback() {
32
- return __awaiter(this, void 0, void 0, function* () {
33
- this.render();
34
- this.embedTweet();
35
- });
36
- }
37
- render() {
38
- const attribute = this.tweetId && heightStore[this.tweetId]
39
- ? `style="min-height: ${encodeURIComponent(heightStore[this.tweetId])};"`
40
- : '';
41
- this.innerHTML = `<div class="${containerClassName}" ${attribute}>
42
- <a href="${this.url}" class="${fallbackLinkClassName}" rel="nofollow">${this.url}</a>
43
- </div>`;
44
- }
45
- embedTweet() {
46
- return __awaiter(this, void 0, void 0, function* () {
47
- const tweetId = this.tweetId;
48
- if (!(this.url && tweetId)) {
49
- console.log(`Invalid tweet URL:${this.url}`);
50
- return;
51
- }
52
- const container = this.querySelector(`.${containerClassName}`);
53
- const disableConversation = this.url.includes('?conversation=none');
54
- window.twttr.widgets
55
- .createTweet(this.tweetId, container, Object.assign({ align: 'center' }, (disableConversation ? { conversation: 'none' } : {})))
56
- .then(() => {
57
- var _a;
58
- /**
59
- * createTweetではJSONPを使っている(?)ためか、catch でエラーハンドリングができない
60
- * => fallback用のリンクをはじめから表示しておき、埋め込みが成功したら削除する
61
- */
62
- (_a = this.querySelector(`.${fallbackLinkClassName}`)) === null || _a === void 0 ? void 0 : _a.remove();
63
- const iframe = this.querySelector('iframe');
64
- if (!iframe)
65
- return;
66
- setTimeout(() => {
67
- heightStore[tweetId] = iframe.style.height;
68
- }, 1000); // 正確な高さを取るために少し待つ
69
- });
70
- });
71
- }
72
- }
73
- exports.EmbedTweet = EmbedTweet;
@@ -1,2 +0,0 @@
1
- declare const initTwitterScriptInner = "window.twttr=(function(f,b,g){var e,c=f.getElementsByTagName(b)[0],a=window.twttr||{};if(f.getElementById(g)){return a}e=f.createElement(b);e.id=g;e.src=\"https://platform.twitter.com/widgets.js\";c.parentNode.insertBefore(e,c);a._e=[];a.ready=function(d){a._e.push(d)};return a}(document,\"script\",\"twitter-wjs\"));";
2
- export default initTwitterScriptInner;
@@ -1,8 +0,0 @@
1
- "use strict";
2
- // Twitterを初期化するためには widgets.js を読み込む必要があるが、
3
- // 公式ドキュメントに記載されている下記のscriptをあらかじめheadなどで読み込んでおけば、
4
- // それ以降ではscriptを改めて読み込まずに window.twtter.createTweet メソッドが使用可能
5
- // ref: https://developer.twitter.com/en/docs/twitter-for-websites/javascript-api/guides/set-up-twitter-for-websites
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const initTwitterScriptInner = `window.twttr=(function(f,b,g){var e,c=f.getElementsByTagName(b)[0],a=window.twttr||{};if(f.getElementById(g)){return a}e=f.createElement(b);e.id=g;e.src="https://platform.twitter.com/widgets.js";c.parentNode.insertBefore(e,c);a._e=[];a.ready=function(d){a._e.push(d)};return a}(document,"script","twitter-wjs"));`;
8
- exports.default = initTwitterScriptInner;
@@ -1,13 +0,0 @@
1
- /**
2
- * 本当はJSONPを使いたくないが、Gistのjsonファイル(https://gist.github.com/foo/bar.json)への
3
- * リクエストでCORSを回避するにはJSONPしか無さそう
4
- *
5
- * 他に検討した方法
6
- * - Gistのscriptをcomponent内で非同期に読み込むことはセキュリティ的に不可能(document.writeが使われているため)
7
- * - iframeだと高さの調整が必要になって面倒
8
- * - Gistが読まれるたびに初期化処理を呼び出すのはアプリ側の処理がややこしい
9
- * => JSONP!!!
10
- *
11
- * ref: https://github.com/moebiusmania/gist-embed
12
- */
13
- export declare function getByJsonp<T>(url: string): Promise<T>;
@@ -1,64 +0,0 @@
1
- "use strict";
2
- /**
3
- * 本当はJSONPを使いたくないが、Gistのjsonファイル(https://gist.github.com/foo/bar.json)への
4
- * リクエストでCORSを回避するにはJSONPしか無さそう
5
- *
6
- * 他に検討した方法
7
- * - Gistのscriptをcomponent内で非同期に読み込むことはセキュリティ的に不可能(document.writeが使われているため)
8
- * - iframeだと高さの調整が必要になって面倒
9
- * - Gistが読まれるたびに初期化処理を呼び出すのはアプリ側の処理がややこしい
10
- * => JSONP!!!
11
- *
12
- * ref: https://github.com/moebiusmania/gist-embed
13
- */
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getByJsonp = void 0;
16
- const head = document.getElementsByTagName('head')[0];
17
- const timeout = 6000;
18
- let timeoutId = null;
19
- function resetTimeout() {
20
- if (timeoutId)
21
- clearTimeout(timeoutId);
22
- }
23
- const clearFunction = (callbackName, callbackId) => {
24
- try {
25
- delete window[callbackName];
26
- }
27
- catch (e) {
28
- window[callbackName] = undefined;
29
- }
30
- const script = document.getElementById(callbackId);
31
- if (script) {
32
- head.removeChild(script);
33
- }
34
- };
35
- function getByJsonp(url) {
36
- const callbackName = `jsonp_${Date.now()}_${Math.ceil(Math.random() * 10000)}`;
37
- const callbackId = `id_${callbackName}`;
38
- return new Promise((resolve, reject) => {
39
- window[callbackName] = (response) => {
40
- resolve(response);
41
- resetTimeout();
42
- clearFunction(callbackName, callbackId);
43
- };
44
- // すでにクエリが付与されている場合は "&" で、付与されていない場合は "?" でcallbackを指定する
45
- const src = url + (url.includes('?') ? '&' : '?') + `callback=${callbackName}`;
46
- const script = document.createElement('script');
47
- script.setAttribute('src', src);
48
- script.setAttribute('charset', 'UTF-8');
49
- script.id = callbackId;
50
- head.appendChild(script);
51
- timeoutId = setTimeout(() => {
52
- reject(new Error(`Request to ${url} timed out.`));
53
- clearFunction(callbackName, callbackId);
54
- window[callbackName] = () => clearFunction(callbackName, callbackId);
55
- }, timeout);
56
- // Catch errors
57
- script.addEventListener('error', () => {
58
- reject(new Error(`JSONP request to ${url} failed`));
59
- clearFunction(callbackName, callbackId);
60
- resetTimeout();
61
- });
62
- });
63
- }
64
- exports.getByJsonp = getByJsonp;