JScript製の簡易 HTML テンプレートエンジン (Webサイト作成時に,画面の共通部品を外部読み込み)
簡易なHTMLテンプレートエンジンのようなもの。
例えばHTMLファイル内に「 #header# 」と書くと,その部分が header.html の内容で置き換えられたような新HTMLが生成される。
つまり,ページ間の共通部分をべた書きしないで済ませるための,よくある置換(include)スクリプト。
というような時に役立つのでは。(サイトのモックをぱっぱと作ってしまいたいなど)
サンプルはこちらからダウンロードできます。
http://www.name-of-this-site.org/codi...
(.batをダブルクリックすると,devフォルダ内の部品をもとに,publishフォルダ内にwebページを作成)
以下WSHのソースコードと,利用例。
ページの雛型:(#〜〜#がincludeを表す)
dev/pages/index.html
<html> <head> <title>トップページ</title> </head> <body> #css# #header# <div id="contents"> ようこそ<br> <a href="./hoge/a.html">こちらへどうぞ</a><br> </div> #footer# </body> </html>
この「#〜〜#」の部分に相当する「部品」を,elementsフォルダに置く。
dev/elements/css.html
<!-- 共通CSS --> <style type="text/css"> a:link { color : #3333CC } a:visited { color : #3333CC } a:hover { color : #3333CC } a:active { color : #3333CC } body{ padding-left : 30px; padding-right: 30px; background-color : dodgerblue; } #contents{ border : solid 1px; border-collapse : collapse; height : 300px; padding : 20px; background-color : aliceblue; } #header{ color : white; text-align : right; } #footer{ padding : 5px; text-align : right; } </style>
dev/elements/header.html
<!-- 共通ヘッダ --> <br> <div id="header"> TEL 03-xxxx-yyyy </div> <br>
dev/elements/footer.html
<!-- 共通フッタ --> <br> <div id="footer"> <a href="mailto:foo@bar">お問い合わせ</a> </div> <br>
そして,上記の部品を組み合わせるスクリプト。
publish_site.js
/* ページ部品を組み合わせて,Webサイト全体のHTMLを構築するスクリプト 使い方: ・publish_site.batをダブルクリック。 ファイル構成: ・dev\elements以下に,共通部品を置く。 ・dev\pages以下に,HTMLを置く。 ・それぞれのHTMLの中では, #(部品名)# と書けば,指定した部品HTMLの内容を読み込める。 例えば, #footer# と書くと,dev\elements\footer.html の内容がそこに埋め込まれる。 */ // ---------- 前処理 ---------- // デバッグ用:ポップアップ出力 function log( s ) { WScript.Echo( s ); } // デバッグ用:標準出力 function out( s ) { var o = WScript.StdOut; o.WriteLine( s ); } // 設定 var dir_dev = "dev"; // 開発用フォルダ var dir_pages = dir_dev + "\\pages"; // ページ var dir_elements = dir_dev + "\\elements"; // 共通部品 var dir_publish = "publish"; // 完成品用フォルダ // 定数 var ForReading = 1; // 読み込み var ForWriting = 2; // 書き込み(上書き) var ForAppending = 8; // 書き込み(追記) // http://language-and-engineering.hatenablog.jp/entry/20081017/1224168811 // このディレクトリのパスを取得 var ws = WScript.CreateObject("WScript.Shell"); var arr_dir_script = WScript.ScriptFullName.split("\\"); // http://wsh.style-mods.net/ref_wscript/index.htm arr_dir_script.pop(); var dir_script = arr_dir_script.join("\\"); //log( dir_script ); // ---------- 共通部品を読み込み ---------- // 全エレメントを読み込み ws.CurrentDirectory = dir_script + "\\" + dir_elements; var proc = ws.Exec("cmd.exe /c dir /s /b *.html"); var res = proc.StdOut.ReadAll().split("\r\n"); // 注:ここで"\n"だけにするとあとで\rが残ってひどい目にあう var fso_r = WScript.CreateObject( "Scripting.FileSystemObject" ); // 登録用の配列 var arr_elems = new Array(); var obj_elems = {}; for( var i = 0; i < res.length; i ++ ) { var elem_name = res[i]; // 有効なファイル名か if( elem_name.length < 1 ) { continue; } //log( elem_name ); // 全行読み出し var txt_r = fso_r.OpenTextFile( elem_name, ForReading ); var str = ""; while( ! txt_r.AtEndOfStream ) { str += txt_r.ReadLine() + "\r\n"; // 注:ここで\r\nの付加を忘れるとあとでひどい目にあう } txt_r.Close(); // ファイル名と内容を登録 var arr_elem_key = elem_name.split("\\"); var elem_key = arr_elem_key[ arr_elem_key.length - 1 ].replace(".html", ""); arr_elems.push( { "name" : elem_key, "str" : str }); obj_elems[ elem_key ] = str; } // エレメント内で置換 for( var i = 0; i < arr_elems.length; i ++ ) { var target_element = arr_elems[i].name; var target_str = arr_elems[i].str; for( var j = 0; j < arr_elems.length; j ++ ) { target_str = target_str.replace( new RegExp( "#(.+)#", "gi" ), // 1 番目にマッチした文字列($1)をキーにして値を返す function(){ return obj_elems[ arguments[ 1 ] ]; } ); // http://language-and-engineering.hatenablog.jp/entry/20080924/1222174957 // http://itmst.blog71.fc2.com/blog-entry-74.html } // 置換済み文字列を再登録 obj_elems[ target_element ] = target_str; } // ---------- ページを生成 ---------- // 全ページを完成品側にコピー ws.CurrentDirectory = dir_script; proc = ws.Exec("cmd.exe /c xcopy /s /h " + dir_pages + "\\* .\\" + dir_publish + "\\"); // xcopy /s /h dev\pages\* .\publish\ // http://language-and-engineering.hatenablog.jp/entry/20081001/1222857265 res = proc.StdOut.ReadAll().split("\r\n"); // 全ページを読み込み ws.CurrentDirectory = dir_script + "\\" + dir_publish; proc = ws.Exec("cmd.exe /c dir /s /b *.html"); res = proc.StdOut.ReadAll().split("\r\n"); // 登録用の配列 var arr_pages = new Array(); var obj_pages = {}; for( var i = 0; i < res.length; i ++ ) { var page_name = res[i]; // 有効なファイル名か if( page_name.length < 1 ) { continue; } // 全行読み出し var txt_r = fso_r.OpenTextFile( page_name, ForReading ); var str = ""; while( ! txt_r.AtEndOfStream ) { str += txt_r.ReadLine() + "\r\n"; } txt_r.Close(); // ファイル名と内容を登録 arr_pages.push( { "name" : page_name, "str" : str }); obj_pages[ page_name ] = str; } // 全ページを置換 var fso_w = WScript.CreateObject( "Scripting.FileSystemObject" ); for( var i = 0; i < arr_pages.length; i ++ ) { var target_page = arr_pages[i].name; var target_str = arr_pages[i].str; for( var j = 0; j < arr_elems.length; j ++ ) { target_str = target_str.replace( new RegExp( "#(.+)#", "gi" ), function(){ return obj_elems[ arguments[ 1 ] ]; } ); } //log( target_str ); // 置換済み文字列を書き込み fso_w.DeleteFile( target_page ); fso_w.CreateTextFile( target_page ); var txt_w = fso_w.OpenTextFile( target_page, ForWriting ); txt_w.WriteLine( target_str ); txt_w.Close(); }
上記のスクリプトを実行するためのバッチ
publish_site.bat
@echo off rem 繰り返し実行可能なコンパイルバッチ rem http://language-and-engineering.hatenablog.jp/entry/20081208/1228708657 :start echo パブリッシュします・・・ rem 完成品用のディレクトリをクリーン if exist "publish" rmdir /s /q publish mkdir publish rem パブリッシュ cscript.exe publish_site.js echo 完了 rem echo. set userkey= set /p userkey=終了する (Enter) / 再度パブリッシュ (p + Enter) ? if not '%userkey%'=='' set userkey=%userkey:~0,1% if '%userkey%'=='p' goto start goto quit rem 終了 :quit
実行すると冒頭の画像のようなページが出力される。
コード概説:
- replaceの第二引数は関数を置ける
- フォルダ階層の確認なし強制消去は rmdir /s /q
- xcopyコマンドを直接走らせている部分があるが,こうやって適宜コマンドの実行結果をWSH中で「流用」すると,コマンドプロンプトの不便さを補ったコードが短く書けると思う。
なお,部品の中で他の部品を読み込むこともできる。ただし1階層だけ。
補足
一般に使われているまともなテンプレートエンジンとしては,JavaならVelocity,PHPならsmartyがある。
Webアプリケーション開発における「テンプレート・エンジン」活用のススメ
http://www.itarchitect.jp/enterprise/...PHP とテンプレートエンジン (Smarty/patTemplate)
http://www.gadgety.net/shin/tips/unix...テンプレートエンジン:JavaScript版ERB、Embedded JavaScript
http://japan.zdnet.com/news/devsys/st...
いずれも変数名を値に展開するパターン。
つまり,素のPHPはそれだけでテンプレートエンジンのようなものだ。
参考:
「独自タグ+コメント」パターン
http://wiki.mesolabo.com/?Log%2F%E4%B...
サーバアクセス時ではなく,ページのファイル生成時にテンプレートを活用する方式を取ったのが今回のスクリプト。
下記のような場合に使う。
HTMLでソースを共通化する
http://qa.asahi.com/qa1502530.htmlHTMLでのWEBページ作成で、「共通モジュール」のような考え方で
ソースを共通化したいと考えています。
どのようにすればよいのでしょうか?
補足
既存のCMS製品の中で,エクスポート機能を持ったものがあればそれで代用できる。
静的HTML出力型のCMS「Geego」
http://www.moongift.jp/2008/07/geego/