はてな記法で<h2>タグを意地でもちゃんと使う
前回、はてな記法にて<h2>
タグをちゃんと使うと言っておきながら使えませんでした。
が!やっぱり<h2>
を使いたい!
使ってやるぜ!
と、いうことで使えるようにしました。
はてな記法エディタ拡張(野良)
chromeマーケットのアカウント持ってないので野良拡張です。
注意事項
使い方
インストールして有効化したら、はてなブログの記事編集ページへボタンが追加されます。
すでに、編集ページを開いていた場合、更新をする必要がありますので一旦記事を保存してブラウザの更新をしてください。
では、ちゃんと動作するか確認をしてみましょう。
はてな記法で、
{ h2 { h3 } }
と、書いて、「HTML」ボタンを押してください。
><div class="section">< ><h2>h2</h2>< ><div class="section">< ><h3>h3</h3>< ></div>< ></div><
と、変換できていたらOKです。
次に、「UnFormat」ボタンを押してください。
変換前へ戻ったらOKです。
書式と解説
{
~}
の間がブロックとして判断されます。
{
と}
は半角で行の先頭にないといけません。
{
の後に続く同じ行の文字列は見出しとして使われます。
}
の後に続く同じ行の文字列は破棄されるので注意してください。
入れ子にした場合、見出しの大きさは自動で調整されます。
見出しの大きさはh2
~h6
までです。5階層以下の見出しサイズは全てh6
となります。
ソースコード
manifest.json
{ "name": "HatenaKihou editor extension", "version": "1.0.0", "manifest_version": 2, "content_scripts": [ { "matches":["https://blog.hatena.ne.jp/*/*/edit*"], "js": ["js/content.js"], "css": ["css/content.css"] } ] }
content.css
.editor-toolbar-extension { padding-bottom: 1px; margin-bottom: 4px; } .toolbar-extension-button{ display: inline-block; font-family: consolas; width: 9rem; height: 2rem; user-select: none; margin-bottom: 3px; } .toolbar-extension-button:hover { cursor: pointer; margin: 0; color: #f80; border-bottom: 3px solid #f80; } .toolbar-extension-button-label { font-family: consolas; font-weight: bold; line-height: 2rem; font-size: 0.9rem; text-align: center; }
ブラウザのバージョンなどは特に考えてないので、もし不具合がありましたら、最悪このファイルは空でも大丈夫です。
ただ、ファイルを削除する場合はマニフェストファイルの編集が必要です。
content.js
//----------------------------------------------------------- // Entry Script //----------------------------------------------------------- var _textElem = null; var toolbars = document.getElementsByClassName("toolbar"); for(var i = 0; i < toolbars.length; i++) { if(toolbars[i].classList.contains("editor-toolbar")) { var container = CreateToolbarExtensionContainer(); // add button container.appendChild(Create_h2_FormatterButton()); container.appendChild(Create_h2_UnFormatterButton()); // insert extension toolbar to hatena toolbar toolbars[i].appendChild(container); } } //----------------------------------------------------------- // Create Functions //----------------------------------------------------------- // // CreateToolbarExtensionContainer // function CreateToolbarExtensionContainer() { var toolbar_ex = document.createElement("div"); toolbar_ex.classList.add("toolbar-extension"); toolbar_ex.classList.add("editor-toolbar-extension"); return toolbar_ex; } // // Create_h2_FormatterButton // function Create_h2_FormatterButton() { return CreateToolButton({"class" : "h2_formatter_bt", "click": On_h2_FormatterClickListener, "label": "HTML"}); } // // Create_h2_UnFormatterButton // function Create_h2_UnFormatterButton() { return CreateToolButton({"class" : "h2_unformatter_bt", "click": On_h2_UnFormatterClickListener, "label": "UnFormat"}); } // // CreateToolButton // function CreateToolButton(args) { var bt = document.createElement("div"); bt.classList.add("toolbar-extension-button"); if(args == null) { return bt; } // set ID if(typeof args.id !== "undefined") { var ids = args.id.split(" "); for(var i = 0; i < ids.length; i++) { bt.classList.add(ids[i]); } } // set Class if(typeof args.class !== "undefined") { var classes = args.class.split(" "); for(var i = 0; i < classes.length; i++) { bt.classList.add(classes[i]); } } // set Click Listener if(typeof args.click !== "undefined") { bt.addEventListener("click", args.click); } if(typeof args.label !== "undefined") { bt.innerHTML = "<p class=\"toolbar-extension-button-label\">" + args.label + "</p>"; } return bt; } //----------------------------------------------------------- // Utils //----------------------------------------------------------- // // ReadTextAreaElement // function ReadTextAreaElement() { if(_textElem == null) { _textElem = document.getElementById("body"); } return _textElem; } // // GetNewLineCharcter // function GetNewLineCharcter(text) { if(text != null && text != "") { // 改行コードの判定と取得 if(text.search(/\r\n/) != -1) { return "\r\n"; } else if(text.search(/\n/) != -1) { return "\n"; } else if(text.search(/\r/) != -1) { return "\r"; } } return ""; } //----------------------------------------------------------- // Listeners //----------------------------------------------------------- // // On_h2_UnFormatterClickListener // function On_h2_UnFormatterClickListener(){ var txtElem = ReadTextAreaElement(); if(txtElem !== null) { var text = txtElem.value; if(text != "") { var c = GetNewLineCharcter(text); if(c == "") { // テキストエリアが空か、一行しかない return; } var v = { nlc: c, isOpenPre: false, isOpenDiv: false, otherDivCount: 0 } var lines = text.split(v.nlc); var current_hsize = 2; text = ""; for(var i = 0; i < lines.length; i++) { if(v.isOpenDiv) { text += lines[i].replace(/^><h[2-6]>(.*)<\/h[2-6]></, "{ $1"); v.isOpenDiv = false; } else if(v.isOpenPre) { if((/^\|\|</).test(lines[i])) { v.isOpenPre = false; } text += lines[i]; } else if((/^>\|.*\|/).test(lines[i])) { v.isOpenPre = true; text += lines[i]; } else if((/^><div class="section"></).test(lines[i])) { if(i + 1 < lines.length) { if((/^><h[2-6]>.*<\/h[2-6]></).test(lines[i+1])){ v.isOpenDiv = true; } else { text += lines[i]; } } else { text += lines[i]; } } else if((/^>?<div.*><?/).test(lines[i])){ text += lines[i]; v.otherDivCount++; } else if((/^>?<\/div><?/).test(lines[i])){ if(v.otherDivCount > 0) { v.otherDivCount--; text += lines[i]; } else { text += lines[i].replace(/^><\/div></,"}"); } } else { text += lines[i]; } if(!v.isOpenDiv) { if(!(/^ *$/).test(lines[i]) || i + 1 < lines.length){ text += v.nlc; } } } txtElem.value = text; } txtElem = null; } } // // On_h2_FormatterClickListener // function On_h2_FormatterClickListener() { var txtElem = ReadTextAreaElement(); if(txtElem !== null) { var text = txtElem.value; if(text != "") { var c = GetNewLineCharcter(text); if(c == "") { // テキストエリアが空か、一行しかない return; } var v = { nlc: c, hsize: 2, isOpenPre: false } var lines = text.split(v.nlc); var current_hsize = 2; text = ""; for(var i = 0; i < lines.length; i++) { if(v.isOpenPre) { if((/^\|\|</).test(lines[i])) { v.isOpenPre = false; } text += lines[i]; } else if((/^>\|.*\|/).test(lines[i])) { v.isOpenPre = true; text += lines[i]; } else if((/^\{ *.*/).test(lines[i])) { current_hsize = v.hsize; if(current_hsize > 6) { current_hsize = 6; } text += lines[i].replace(/^\{ *(.*)/, "><div class=\"section\"><" + v.nlc + "><h" + current_hsize + ">$1</h" + current_hsize + "><"); v.hsize++; } else if((/^\}/).test(lines[i])) { current_hsize = v.hsize; if(current_hsize > 6) { current_hsize = 6; } text += lines[i].replace(/^\}.*/,"></div><"); v.hsize--; } else { text += lines[i]; } if(!(/^ *$/).test(lines[i]) || i + 1 < lines.length){ text += v.nlc; } } for(;v.hsize > 2; v.hsize--) { text += "></div><" + v.nlc; } txtElem.value = text; } txtElem = null; } }
<textarea>
から文字を読み込んで1行ずつ判定するという地獄みたいな実装になっています。
「HTML」ボタンがクリックされるとOn_h2_FormatterClickListener
が、「UnFormat」ボタンをクリックでOn_h2_UnFormatterClickListener
が実行されます。
最後に
ソースコードの中身と、そこまでしてセクションを分けたいか?という現実はさて置き、中々使えるものが出来たのではないでしょうか?
これでやっと<h2>
が使えるようになりました。
最後になりますが、この記事で紹介しているコードを使用する際は自己責任でおねがいします。