JavaScriptでヒアドキュメントや簡易テンプレート
JavaScriptでもヒアドキュメントやテンプレートが使えれば便利だし、それらを組み合わせてコードの塊(スニペット)にできれば便利だよね。
ってことで、既に同様のアイデアはゴロゴロしてそうなんですが、自分用にざざっと叩き台を書いてみました。
DEMO
http://pigs.sourceforge.jp/blog/20100505/test/demo.snippet/uu.snippet.htm
<!doctype html><html><head><meta charset="utf-8" /> <title></title> <script src="src/uupaa.js"></script> <script src="src/uu.snippet.js"></script> <script> function xboot(uu) { var arg = { PHPStyleArgs: "PHPStyleArgs", RubyStyleArgs: "RubyStyleArgs", DualBraceStyle: 'style="color: green; background-color: #ccc"', key: "implement template.key", value: "implement template.value", list: { length: 3, key: ["key1", "key2", "key3", "key4"], value: ["value1", "value2", "value3", "value4"] } }; alert( uu.snippet("snippet1", arg) ); alert( uu.snippet("snippet2", arg) ); uu.body( uu.snippet("snippet3", arg) ); uu.body( uu.snippet("snippet4", arg) ); } </script> </head><body></body></html> <script id="snippet1" type="text/html"> // --- PHP style text here document --- return <<<EOS "The PHP" style here document. text only; <div> does't work. implement {$arg.PHPStyleArgs} args EOS; </script> <script id="snippet2" type="text/html"> // --- Ruby style text here document --- return <<EOS 'The Ruby' text here document. text only; <div> does't work. implement #{arg.RubyStyleArgs} args EOS </script> <script id="snippet3" type="text/html"> // --- text here document --- return <text> text here document. text only; <div> does't work. implement {{arg.DualBraceStyleArgs}} args </text> </script> <script id="snippet4" type="text/html"> // --- complex here document --- return <> <div {{arg.DualBraceStyle}}> <ul> <each arg.list> <li {{arg.ListStyle}}>{{key}}</li> <li>{{value}}</li> </each> </ul> </div> </> </script>
できること
- uu.snippet(id, arg) を実行すると <script id="スニペットID" type="text/html">JavaScriptExpression</script> を読み込み、引数argを与えてコードを評価します。
- 引数には Hash や Array など好きな値を渡せます。スニペット内では、引数は "arg" という名前で参照できます。
- PHPスタイル, Rubyスタイル, <text>...</text> スタイルでヒアドキュメントを定義できます。
- <>...</> で、DOMノードを生成できます
- <each ident> ... </each> で囲んだ範囲は、ident.length の回数だけ繰り返し評価/置換します。
uu.snippet.js
// === snippet === //{{{!depend uu //}}}!depend uu.snippet || (function(uu) { uu.snippet = uusnippet; // uu.snippet - evaluate snippet function uusnippet(id, // @param String: snippet id. <script id="..."> arg) { // @param Mix(= void): arg // @return String/Mix: var ld = uusnippet, js = ld.stock[id] || "", node; if (!js) { node = uu.id(id); if (!node) { return ""; } js = node.text.replace(ld.toNewLine, "\n"). replace(ld.PHPStyleHereDocument, function(all, eos, match) { return '"' + normalize(match).replace(ld.DualBrace, implant). replace(ld.PHPBrace, implant) + '"'; }).replace(ld.RubyStyleHereDocument, function(all, eos, match) { return '"' + normalize(match).replace(ld.DualBrace, implant). replace(ld.RubyBrace, implant) + '"'; }).replace(ld.TextHereDocument, function(all, match) { return '"' + normalize(match).replace(ld.DualBrace, implant) + '"'; }).replace(ld.ComplexHereDocument, function(all, match) { match = normalize(match, 1). replace(ld.eachBlock, function(all, hash, block) { return '" + uu.snippet.each(' + hash + ',"' + block.replace(uusnippet.DualBrace, toEachHash) + '") + "'; }).replace(ld.DualBrace, implant); return 'uu.node.bulk("' + match + '");'; }).replace(ld.trimNewLine, ""); uusnippet.stock[id] = js; } return js ? (new Function("arg", js))(arg) : ""; } uusnippet.stock = {}; // { id: JavaScriptExpression, ... } uusnippet.toNewLine = /\r\n|\r|\n/g; uusnippet.PHPStyleHereDocument = /<<<(\w+)\n([\s\S]*?)^\1;$/gm; uusnippet.RubyStyleHereDocument = /<<(\w+)\n([\s\S]*?)^\1$/gm; uusnippet.TextHereDocument = /<text>\n([\s\S]*?)^<\/text>$/gm; uusnippet.ComplexHereDocument = /<>\n([\s\S]*?)^<\/>$/gm; // {{{ uusnippet.PHPBrace = /\{\$([^\}]+)\}/g; uusnippet.RubyBrace = /#\{([^\}]+)\}/g; uusnippet.DualBrace = /\{\{([^\}]+)\}\}/g; uusnippet.EachBrace = /\{\(([^\)]+)\)\}/g; uusnippet.trimNewLine = /^\s*\n\s*|\s*\n\s*$/g; uusnippet.trimMultiLine = /^\s+|\s+$/gm; uusnippet.trim = /^\s+|\s+$/g; uusnippet.eachBlock = /<each ([^>]+)>([\s\S]*?)<\/each>/; uusnippet.escapeQuote = /("|')/g; uusnippet.escapeNewLine = /\n/g; uusnippet.arg = /^arg\./; uusnippet.each = each; // uu.snippet.each function each(hash, // @param Hash: { length: 3, prop1: [value, ...], prop2: [value, ...], ... } fragment) { // @param String: "<li>{(prop1)}</li><li>{(prop2)}</li>" // @return String: var i = 0, iz = hash.length, block = []; for (; i < iz; ++i) { block.push(fragment.replace(uusnippet.EachBrace, function(all, ident) { return hash[ident][i]; })); } return block.join(""); } // inner - function implant(all, ident) { return '"+' + ident + '+"'; } // inner - "{{ident}}" -> "{(ident)}" function toEachHash(all, ident) { if (uusnippet.arg.test(ident)) { return '"+' + ident + '+"'; } return '{(' + ident + ')}'; } // inner - function normalize(str, multiLine) { return str.replace(multiLine ? uusnippet.trimMultiLine : uusnippet.trim, ""). replace(uusnippet.escapeQuote, "\\$1"). replace(uusnippet.escapeNewLine, "\\n"); } })(uu);
もっとお手軽に
もっとお手軽な方法が欲しい方は、各行末にバックスラッシュ(\)を書けば、ヒアドキュメントの代用になります。
<!doctype html><html><head><title></title><script> var here = "text here document.\ text only;\ <div> does't work."; alert(here); </script></head><body></body></html>