ホームページ > ウェブフロントエンド > jsチュートリアル > Trello Chrome拡張機能の構築方法 - リストのエクスポート

Trello Chrome拡張機能の構築方法 - リストのエクスポート

William Shakespeare
リリース: 2025-02-20 11:23:13
オリジナル
475 人が閲覧しました

Trello Chrome拡張機能の構築方法 - リストのエクスポート

前の部分では、拡張機能の基本を構築し、カスタムファンデーション駆動の設定画面を介して認証を実装し、TrelloのJavaScriptクライアントライブラリを使用しました。この部分では、エクスポートロジックとUIを追加して拡張機能を終了します。

キーテイクアウト

    `chrome.extension.sendmessage` APIを使用して、Chrome拡張機能の設定ページと背景ページの間でセキュアトークン転送を転送します。
  • 最初のインストール時に、または認証なしでTrelloボードにアクセスしたときに、拡張機能の設定ページを自動的に開きます。
  • TrelloのUIにカスタムメニューオプションを統合して、jQueryを使用して動的なDOMの変更を処理し、リストのエクスポートを有効にします。
  • トレロUIの制限のために必要なリスト内のカード要素の検査を含む回避策を通じてTrelloリストIDを取得します。
  • バックグラウンドスクリプトでメッセージの渡されて、リストIDが取得されたら、TrelloのAPIからリストカードを取得するように実装してください。
  • TXTとJSON形式の両方でエクスポートオプションを提供し、カスタムビルドモーダルポップアップを使用してエクスポートデータを表示し、TrelloのネイティブスタイルとのCSS競合を回避します。
  • メッセージング
  • 設定画面でTrelloで認証すると、Trelloトークンはローカルストレージに保存されます。ただし、設定ページは独自のページであり、事実上、独自の環境です。Ergo、拡張機能の背景ページも拡張機能のコンテンツスクリプトもアクセスできません。これは、メッセージの合格を使用する必要がある場所です
  • Chrome.extension.sendmessage APIは、バックグラウンドページからのメッセージを送信するために使用されます。私たちの場合、それを使用して、設定ページから背景ページにトークンを送信します。 設定の冒険はこれが完了するとすぐに行われるので、ユーザーの親しみやすさを向上させるために、タブを自動的に閉じることもできます。
  • settings.jsのinit関数の最初の部分をこれに更新します:
このロジックを使用して、認証が完了したときにTrelloライブラリに拡張機能にメッセージを送信するように指示し、メッセージが受信されたという返されたメッセージ(関数(データ)の部分)が受信されるとすぐに、閉じます。現在のタブ。

次に、背景ページを扱いましょう。まず、background.htmlの内容をこれに変更します:

アプリキー、ロジックに使用するバックグラウンドスクリプト、および以前のようにTrelloクライアントをロードします。明らかに、私たちもjQueryが必要です - それはTrelloの依存関係です。

次に、スクリプト/background.jsを変更してください

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
これは、[設定]ページからメッセージを受信する部分です。リクエストからトークンをつかみ、将来の使用のためにLocalStorageに保存します。コマンドを備えたオブジェクトフォーメーションをメインキーとして使用します。なぜなら、後で他のコマンドをページに送信するつもりであるためです。

auto-settings

Savetokenコマンドの上に、別のブロックを見てみましょう:

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
特定のコマンドを発行せず、ユーザーがTrelloでまだ認証されていない場合は、新しいタブで[設定]ページを開きます。これにより、ブラウザでTrelloボードが訪問されるとすぐに、拡張機能の最初のインストール後すぐに設定ページが開かれるようになります。

メニューオプションの追加

TrelloのUIは、カスタマイズに非常に友好的ではありません。リストには、データの属性またはあらゆる種類のリンクなど、要素にIDがありませんが、カードはありません。右上隅のリストオプションボタンをクリックすると生成されるコンテキストメニューは、呼び出されるたびにすべてゼロから再構築されます(多くのことが多すぎますか?)。また、UIの他のほとんどすべてのメニューをクリックすると召喚されました。さらに悪いことに、リストのポップオーバーメニューを呼び出すと、メニュー自体に呼び出されたリストの識別子がないため、そのコンテキストについてはわかりません。 Trello APIそれについては、エクスポートのためにカードを取得します。これが、続くものがひどい多くの魚のようなハッカーのように見えるかもしれない理由ですが、それは、まあそうだからです。

[コンテキスト]メニューにメニューオプションを追加するには、main.jsコンテンツスクリプトを編集する必要があります。これをこれに変えてください:

var popover = $( "。pop-over");から始めて、ポップオーバーオブジェクトを保持するように変数を設定します。次に、リストのメニューボタンがクリックされたとき(.list-header-menu-icon)、ポップオーバーが表示されているかどうかを常に監視する間隔を召喚します。目に見えるようになると、チェックの停止とメニューオプションがすべてのオプションの下部に追加され、特に残りのように見えるように作成されているため、フィットします。最後に、クリックイベントハンドラーがこのオプションにバインドされているため、オプションがクリックされたら、「エクスポート」を呼び出します。しかし..輸出するために必要なものをどうやって知るのですか?また、どの形式でエクスポートしていますか? リストIDを見つける

<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
前に言ったように、TrelloのUIは開発者ではないことで有名です。 DOM要素を備えたリストIDを提供していないため、それらを見つけるのは簡単ではありません。なぜリストIDが必要なのですか? Trello APIをクエリしてカードを取得するために、それらをエクスポートできるように - 私たちはすでに大規模なボード上のその不安定性のためにUIを解析するつもりはないが、APIに依存するつもりです。

幸いなことに、個々のリストでカードを検査すると、実際にはHREF属性があり、カードIDが含まれていることがわかります。カードIDを知ることで、Trelloの情報を照会し、親リストのIDを取得できます。しかし..ポップオーバーメニューがリストに添付されていない場合、クリックしたリストをどのように見つけますか?出会った最初のカードをつかむことはできません。それはあまりにもランダムになります。

メニューボタンがクリックされたときにjQueryによって発射されたイベントを使用できます。これは重要です! [エクスポート]オプションをクリックする代わりに、メニューの元のクリックボタンを使用します。これは、元のボタンがエクスポートに関心のあるリストにバインドされている間、スポークされる実際のメニューはそうではないので、そのため、どのリストを扱っているかを見つけることをほぼ不可能にします。 //上記のコードの//エクスポートリストのコメントの代わりに、これを追加します:

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

次に、関数を作成します:

<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

最後に、findFirstCardid関数を作成します:

chrome<span>.extension.onMessage.addListener(
</span>    <span>function (request<span>, sender, sendResponse</span>) {
</span>        chrome<span>.pageAction.show(sender.tab.id);
</span>
        <span>// Now we have a token saved locally, as fetched from the settings page after authorization.
</span>        <span>if (request.command == 'saveToken') {
</span>            <span>localStorage.setItem('trello_token', request.token);
</span>            <span>sendResponse();
</span>            <span>return true;
</span>        <span>}
</span>
    <span>});</span>
ログイン後にコピー

イベントのターゲット(リスト)の祖父母を取得し、その中に最初のカードタイトルを見つけます。タイトルには、この形状のhrefが含まれています:

Trello Chrome拡張機能の構築方法 - リストのエクスポート

タイトルが見つからなかった場合、リストをエクスポートできないことをユーザーに警告します。それ以外の場合は、カードのIDを抽出して返します

エクスポート機能がカードIDを持っているので、それを使用してリストIDを見つけることができます。 APIドキュメントを見ると、URLカード/{{{id}}を使用して必要なものを取得できます。 Trelloを返すように依頼するデータの量を最小限に抑えるために、Fields Paramを使用してクエリをidlistプロパティのみに制限することもできます。 background.js。

に新しいコマンドを追加しましょう

<span>if (!request.command && !localStorage.getItem('trello_token')) {
</span>            chrome<span>.tabs.create({url: chrome.extension.getURL('settings/index.html')});
</span>            <span>sendResponse();
</span>            <span>return true;
</span>        <span>}</span>
ログイン後にコピー
Trelloinit関数も定義する必要があります。これは、Trelloと対話するコマンドが呼び出される前に毎回呼び出すことができるため、トークンとキーが正しく設定されており、リクエストが認証されていることを100%確信しています。

リストIDを正常に取得するようになりました。
chrome<span>.extension.sendMessage({}, function (response) {
</span>    <span>var readyStateCheckInterval = setInterval(function () {
</span>        <span>if (document.readyState === "complete") {
</span>            <span>clearInterval(readyStateCheckInterval);
</span>
            <span>var popover = $(".pop-over");
</span>            <span>$('.list-header-menu-icon').click(function(event) {
</span>                <span>var popover_summoned_interval = setInterval(function () {
</span>                    <span>if ($(popover).is(':visible')) {
</span>                        <span>clearInterval(popover_summoned_interval);
</span>                        <span>$(".pop-over .content").append('<hr><ul > <li><a  href="#">Export This List</a></li> </ul>');
</span>                        <span>$(".js-export-list").click(function(e){
</span>                            <span>// EXPORT LIST
</span>                        <span>});
</span>                    <span>}
</span>                <span>}, 50);
</span>            <span>});
</span>        <span>}
</span>    <span>}, 10);
</span><span>});</span>
ログイン後にコピー

リストカードの取得

メイン.jsに戻って、さらに数行のコードを使用して、次のように見えるエクスポート機能があります。

「Humanese」では、これは次のとおりです

最初のカードのIDを取得します
<span>exportList(event);</span>
ログイン後にコピー

IDが見つからない場合、リストは明らかに空です

    IDが見つかった場合は、背景ページを呼び出して、Trello APIへの呼び出しを介してリストIDを指示するように指示してください
  • リストIDが問題ない場合は、リストのカードを取得するバックグラウンドページに別の呼び出しを行い、完了した場合、結果をコンソールに出力します。
  • バックグラウンドページに戻ると、APIドキュメントに従って、getListCardsコマンドを構築できるようになりました。
  • 拡張機能をリロードしてテストすると、リストにエクスポートオプションが表示されるだけでなく、オプションをクリックした後にコンソールでエクスポートしているデータも表示できるはずです。
  • エクスポートフォーマット
  • このチュートリアルはそのように少し長く実行されているため、今のところ、エクスポートに簡略化されたアプローチを取ります。ユーザーにTXTまたはJSONのいずれかの選択を提供します。テキスト出力は、今のところ、次のようになります

JSONはTrelloから受け取ったように満足します。

Trello Chrome拡張機能の構築方法 - リストのエクスポート

JSONデータは明らかにはるかに多くの情報を生成しますが、編集するのもはるかに簡単です。JSONエディターオンラインまたはJSONからCSVへの任意のIDEまたはツールに貼り付けてください。 >

エクスポートするには、データを貼り付けるモーダルウィンドウが必要です。 ここでの魅力的なオプションは、設定ページに既に使用しており、独自のモーダルポップアップコンポーネントを備えているため、Foundation Frameworkですが、FoundationのCSSもTrelloのCSSも適切に名前が付けられておらず、Trelloの原因の原因にFoundationのCSSを含めています。また、jQueryが事前に組み込まれていますが、繰り返しますが、ダイアログを起動して実行するには、JQuery UIを含める必要があります。それでも十分ではありません。Chrome拡張機能は、相対URLを介したCSSのロード画像をサポートしていません( )JQUERY UIが使用する構文 - jQuery UIのCSSを書き換えてローカル拡張URLを使用するか、Base64エンコード画像を使用する必要があります。アプローチ。

代わりに、私たちは自分のポップアップを作成し、Trelloの既存のスタイルのいくつかを使用して、途中ですべての競合を無視します。ここに最終コードを入れて、それを説明します。 lib/trellohelper/js/exportpopup.jsを作成し、次の内容を付けてください

メインスクリプトの外側にポップアップロジックを使用することを選択したため、後で簡単に改善できます。また、「オブジェクト指向」のアプローチを選択しました。 INIT、表示、非表示の3つの方法で、新しいTrelloExportPopup「クラス」を定義します。 INITは、コンテンツスクリプトがロードされるとすぐに呼び出されます。これは、ポップアップを構築し、適切なイベントリスナーを添付し、すべてをTrelloボードのHTMLに追加する方法です。ポップアップのヘッダーのボタンに.buttonクラスを追加すると、現在のTrello UIと一致する外観が確実に得られます。ここで私が試している外観は、一種の「タブ付き」インターフェイスです。テキストとテキストエクスポートショーをクリックして、JSONとJSONをクリックしてください。
<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
非表示メソッドは、ページのどこかに目に見える形で存在する場合にのみポップアップを非表示にします。 showメソッドは、最初の(json)タブビューを自動的にアクティブにし、エクスポート領域に必要なデータを導入します。 JSON領域は単純なStringifyダンプです。JSONデータの出力は文字列形式ですが、テキスト領域は今のところ、カードの各カードのタイトルと説明を、カード間に2つの空の行で出力します。 「コピーペーストフレンドリー」。

今やらなければならないのは、少しスタイルです。これは、LIB/TrelloHelper/CSS/ExportPopup.cssの内容です

これにより、ポップアップが中央にあり、ネイティブトレロポップアップのように見えることを確認します。また、輸出コンテンツがポップアップの残りのスペースを埋めることを示すTextareaが確認されます。次に、これらのファイルをコンテンツスクリプトに含めてみましょう:

<span>// Check if page load is a redirect back from the auth procedure
</span>    <span>if (HashSearch.keyExists('token')) {
</span>        <span>Trello.authorize(
</span>            <span>{
</span>                <span>name: "Trello Helper Extension",
</span>                <span>expiration: "never",
</span>                <span>interactive: false,
</span>                <span>scope: {read: true, write: false},
</span>                <span>success: function () {
</span>                    chrome<span>.extension.sendMessage({
</span>                        <span>command: 'saveToken',
</span>                        <span>token: localStorage.getItem('trello_token')
</span>                    <span>}, function(data) {
</span>                        chrome<span>.tabs.getCurrent(function (tab) {
</span>                            chrome<span>.tabs.remove(tab.id)
</span>                        <span>});
</span>                    <span>});
</span>                <span>},
</span>                <span>error: function () {
</span>                    <span>alert("Failed to authorize with Trello.")
</span>                <span>}
</span>            <span>});
</span>    <span>}</span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

最後に、新しいポップアップロジックを使用してmain.jsをスパイスしましょう。 main.jsの最終バージョンは次のようになります:

<span><span><!doctype html></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/key.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="scripts/background.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/jquery-2.1.1.min.js"</span>></span><span><span></script</span>></span>
</span><span><span><span><script</span> type<span>="text/javascript"</span> src<span>="lib/trello_client.js"</span>></span><span><span></script</span>></span></span>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

最初にtrelloexportpopupを「インスタンス化」するため、コードでそのメソッドを使用できます。次に、クリックイベントリスナーをメニューにバインドする前に、TEP.INIT()でポップアップを初期化するため、必要になる前にDOMで準備ができています。エクスポートリンクがクリックされた後、以前と同じようにエクスポートリストを呼び出します。 エクスポートリストの関数では、別のリストのメニューを閲覧しているときに1つが開いている場合に、最初にTEP.hide()でポップアップを非表示にします。次に、バックグラウンドページからカードを取得したら、表示します。 Tep.show(データ)を使用したエクスポートポップアップ。それでおしまい!

拡張機能をリロードして、trelloページを更新すると、機能するエクスポートオプションが必要です!

Trello Chrome拡張機能の構築方法 - リストのエクスポートバグと改善

意図的にいくつかのバグと警告を残しました。十分な関心があれば、将来の投稿の人々に対処し、フェイルセーフティの拡張機能を微調整して最適化します。まだ可能ないくつかの改善点は次のとおりです

キャッシュ

将来の用途のために物事をスピードアップするために、LocalStorageを使用して、リストがどのボードに属しているかを覚えておくことができます。リストをボードからボードに移動すると壊れる可能性があるため、この機能を慎重に実装してください。リストの動きに別のリスナーを追加してください。

重複したエクスポートオプションスポーン

メニューが開いている間にメニューアイコンをマニアックにクリックすると、メニューの下部に新しい「エクスポート」オプションを追加し続けます。 OptionがすでにそこにあるかどうかをチェックするFailSafeを実装する必要があります。

initの問題

何百ものボードとメンバーがいる巨大なボードでは、TrelloのUIはひどく遅くなります。これにより、ドキュメント対応イベントの不火が発生し、リスナーにバインドするUI要素がある前に、スクリプトのinit部分が実行されます。そのため、メニューにはエクスポートオプションがなく、更新されるまで取得できません。

ボードの変更

ボードの変更は現在のUIを殺し、新しいボードのために再構築します。ただし、問題は、イベントリスナーの要素も殺されることです。そのため、メニューはエクスポートオプションを召喚しなくなりました。上記の問題と同様に、すべてが機能するために、再生化を船上の変更にトリガーする必要があります。

inifinite loop

メニュークリック後にポップオーバーがレンダリングされない天文学的に小さなチャンスがあります。TrelloがUIで何かを変更したり、クラスを変更したり、ある種のUIバグを持っているだけかもしれません。可視性をループチェックすることは無限になり、タブのプロセスが殺されるまで膨大な量のCPUリソースを占有します。それに対する保護手段はいいでしょう。

結論

この短いシリーズでは、Trello用のシンプルなChrome拡張機能を構築しました。これにより、特定のリストからカードをJSONまたはTXTリストとしてエクスポートできます。この例を使用して構築し、独自のTrello拡張機能を作成します。達成できることは、想像力(およびTreloのAPIが提供する機能)によってのみ制限されています。認証はすでにあなたのために解決されており、ロジックテンプレートが整っています - コーディングを開始してください!

このチュートリアルシリーズで書いたコードは、githubで入手できます。

このチュートリアルの継続を見たいですか?その他の機能が実装されていますか?お知らせ下さい!フィードバックは高く評価されています!

Trelloリストのエクスポート

に関するよくある質問(FAQ)

TRELLOリストをExcelにエクスポートするにはどうすればよいですか?はい、「Trelloのエクスポート」Chrome Extensionを使用してTrelloリストをエクスポートするとき、すべてラベルを含むTrelloリストの詳細がエクスポートされます。これは、Trelloリストをエクスポートした後でも、ラベルを追跡できることを意味します。

以上がTrello Chrome拡張機能の構築方法 - リストのエクスポートの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート