URL表記に a要素が自動生成される問題
アメブロの通常表示編集枠に「https://blog.ameba.jp」などの「URL」の文字列をペーストすると、Chromeでは自動的に「a要素」が生成されます。 Firefoxは動作が異なり、コピー元が単なる文字列の場合は「a要素」は生成されません。
下は、「Tampermonkey」のコード編集画面から、アメブロの編集枠にコードをペーストしたサンプルです。
「UserScript」のパラメータ部の青い文字列は、「Tampermonkey」側には無かった「a要素」が自動的に生成され、リンクになった事を示しています。
「HTML表示」に切り替えると、下の様に盛大に「 」が生成されています。
下は「To Space(nbsp)」で「 」を半角空白に置換えた所です。
コード内容が判り易くなりましたが、青枠はペースト時に生成された「a要素」です。
「pre枠」の目的に支障はありませんが、この「a要素」は不要でいただけません。 実は「Tampermonkey」のスクリプト記事を書くたび、このリンクを削除しています。
アメブロの編集枠は、リンク表示をクリックして「a要素」を削除出来ます。 これは気の利いた大変良い機能です。 しかし、スクリプトコードのずっと下方で「a要素」が勝手に生成されると、それを見落としかねません。 そこで、この類の「a要素」を自動削除する機能も実装しました。
a要素の自動削除
整形エンジンの主要部ですが、これまでのコードに青文字部分を割込ませています。
「pre枠」の子ノードで「nodeType==3」のテキストノードは変換処理。 それ以外の子ノードで、「nodeType==1」の要素ノードで且つ、タグ名が「A」の場合に、問題とする「a要素」と、第一段階の判断をします。
更に、「a要素」の子ノードが1個で textノードであれば、自動生成された「a要素」と断定して、「a要素」をその内部の「textノード」で置き換えます。 それ以外の複雑な構成の「a要素」の場合は、自動生成されたものではなく、何らかのコード内容と考えて、処理をせずに「a_count」でその例外の個数を数え、後で報告します。
想定外の「a要素」がコード内にあるかも知れないので、安易にはテキスト置換の処理をしない様にしています。
a要素の自動削除の実際の動作
下は、「Tampermonkey」のユーザースクリプトの @パラメーター部で、標準的な例で、自動生成された「a要素」が2個あります。
新しい整形コードの処理は、2個の「a要素」を普通のテキストに置変えます。
意図的に「a要素」にリンクアイコンを付加したものをテストすると、その「a要素」だけは下の様に処理されずに残ります。
この様な問題があった場合は、「 」と「a要素」の処理を同時に行った後で、それぞれ例外で処理されずに残った数を報告する様にしました。
例外がなく処理が完了した場合は、青枠を表示するだけにしています。
通常表示の編集画面から移動しない限り、処理した枠を元の状態に戻せます。 この復帰方法は少し改めて、処理例外の有無にかかわらず、常に元に戻す様にしています。
BR Checker / To Space (nbsp) のコード
〔 BR Checker / To Space (nbsp) 〕ver. 0.7
// ==UserScript== // @name BR Checker / To Space (nbsp) // @namespace http://tampermonkey.net/ // @version 0.7 // @description Blogの書式整形ツール // @author Ameba blog User // @match https://blog.ameba.jp/ucs/entry/srventry* // @grant none // ==/UserScript== window.addEventListener('load', function(){ 'use strict'; var marked1=0; // BR Checker 起動・非起動の指標 var marked2=0; // To Space (nbsp) 起動・非起動の指標 var target; var editor_iframe; var iframe_doc; var iframe_body; var pre_box; // PRE枠のコンテナブロック target=document.getElementById('cke_1_contents'); // 監視 target let monitor=new MutationObserver(catch_key); monitor.observe(target, {childList: true}); // ショートカット待受け開始 catch_key(); function catch_key(){ let send; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ // WYSIWYG表示が実行条件 if(marked1==1){ sign(); } if(marked2==1){ active(); } iframe_doc=editor_iframe.contentWindow.document; iframe_doc.addEventListener("keydown", check_key); document.addEventListener("keydown", check_key); function check_key(event){ let gate=-1; if(event.which==17 || event.ctrlKey==true){ if(event.which==120 || event.keyCode==120){ event.preventDefault(); send=120; gate=1; }} // ショートカット「Ctrl+F9」 if(event.which==17 || event.ctrlKey==true){ if(event.which==121 || event.keyCode==121){ event.preventDefault(); send=121; gate=1; }} // ショートカット「Ctrl+F10」 if(editor_iframe){ if(gate==1){ event.stopImmediatePropagation(); event.preventDefault(); set_task(send); }}} // check_key }} // catch_key function set_task(send){ if(send==120){ //「Ctrl+F9」 BR Checker スイッチ if(marked1==0 && marked2==0){ marked1=1; sign(); } // BR Checker をON else if(marked1==1){ marked1=0; sign_clear(); }} // BR Checker をOFF if(send==121){ //「Ctrl+F10」 To Space (nbsp) スイッチ if(marked1==0 && marked2==0){ marked2=1; active(); } // To Space (nbsp) をON else if(marked2==1){ marked2=0; disable(); }} // To Space (nbsp) をOFF } // set_task // ********** BR Checker Functions ************** function sign(){ let br_tag; let br_mark; let i_tag; let c_br=0; let css=''; let disp; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); editor_iframe.onload=function() { iframe_doc=editor_iframe.contentWindow.document; } i_tag=iframe_doc.querySelectorAll('.brmark'); if(i_tag.length >=1){ for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }} // BRマーク削除 br_tag=iframe_doc.querySelectorAll('br'); for(let i=0; i < br_tag.length; i++){ if(br_tag[i].parentNode.tagName=="P" && br_tag[i].parentNode.childNodes.length !=1){ c_br +=1; br_mark=iframe_doc.createElement("i"); br_mark.appendChild(iframe_doc.createTextNode("▼")); br_mark.setAttribute("class", "brmark"); br_mark.setAttribute("style", "display: inline-block;width: 0;margin-left: -2px;color: red"); br_tag[i].parentNode.insertBefore(br_mark, br_tag[i]); } else{ continue; }} // BRマーク表示 css +=['display: inline-block; margin: 0 0 4px 8px; padding: 4px 15px 1px;', 'font-size: 16px; color: #fff; background: red'].join(' '); disp=document.createElement("span"); disp.appendChild(document.createTextNode("▼ BR Checker")); disp.setAttribute("style", css); disp.appendChild(document.createTextNode(" count : " + c_br)); monitor.disconnect(); if(target.firstChild.tagName!="IFRAME"){ target.firstChild.remove(); target.insertBefore(disp, editor_iframe); } else{ target.insertBefore(disp, editor_iframe); } monitor.observe(target, {childList: true}); } // BR Checker 起動表示 function sign_clear(){ let i_tag; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); iframe_doc=editor_iframe.contentWindow.document; if(target.firstChild.tagName !="IFRAME"){ target.firstChild.remove(); } // BR Checker 起動表示を削除 i_tag=iframe_doc.querySelectorAll('.brmark'); if(i_tag.length >=1){ for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }}} // BRマーク削除 // ********** To Space (nbsp) Functions ************** function active(){ let buffer; let nbsp; let a_count; let css=''; let disp; monitor.disconnect(); css +=['display: inline-block; margin: 0 0 4px 8px; padding: 4px 50px 1px;', 'font-size: 16px; color: #fff; background: #03a9f4'].join(' '); disp=document.createElement("span"); disp.appendChild(document.createTextNode("To Space (nbsp)")); disp.setAttribute("style", css); if(target.firstChild.tagName=="IFRAME"){ target.insertBefore(disp, editor_iframe); } // To Space (nbsp) 起動表示 monitor.observe(target, {childList: true}); iframe_doc=editor_iframe.contentWindow.document; iframe_body=iframe_doc.querySelector('body.cke_editable'); pre_box=iframe_body.querySelectorAll('div'); buffer=Array(pre_box.length); for(let i=0; i<pre_box.length; i++){ select_pre_box(i); } function select_pre_box(i){ pre_box[i].oncontextmenu=function(){ if(pre_box[i].firstChild.tagName=='PRE'){ select_box(select_do); return false; function select_box(select_do){ boxshadow(); setTimeout( select_do, 100); } function boxshadow(){ pre_box[i].style.outline='2px solid #03a9f4'; } function select_do(){ start_select(i); }}}} function start_select(i){ let regex=new RegExp('\u00A0', 'g'); nbsp=pre_box[i].innerText.match(regex); let pa_count=pre_box[i].getElementsByTagName("a"); if(buffer[i] !=null){ let ok=confirm(" ❎ 変換前に戻しますか?"); if(ok){ pre_box[i].firstChild.innerHTML=buffer[i]; buffer[i]=null; pre_box[i].style.outline='none'; }} else{ if(nbsp !=null || pa_count !=null){ let ok=confirm([" ⏬ 「 」 " + nbsp.length + " 個を半角空白に変換します\n", " 「a要素」 " + pa_count.length + " 個をテキストに置換えます"].join('')); if(ok){ to_space(i); } else{ pre_box[i].style.outline='none'; }} else{ ; }}} function to_space(i){ a_count=0; buffer[i]=pre_box[i].firstChild.innerHTML; // 選択pre枠のデータバックアップ let child_node=pre_box[i].firstChild.childNodes; for(let t=0; t<child_node.length; t++){ if(child_node[t].nodeType==3){ child_node[t].nodeValue=child_node[t].nodeValue.replace(/\u00A0/g , '\u0020'); } else if(child_node[t].nodeType==1 && child_node[t].tagName=="A"){ let inner_node=child_node[t].childNodes; if(inner_node.length==1 && inner_node[0].nodeType==3){ pre_box[i].firstChild.replaceChild(inner_node[0], child_node[t]); } // text を a要素と入替え else{ a_count +=1; }} // 子ノードが1個の textの条件に当てはまらない a要素 else{ let child_node2=child_node[t].childNodes; if(child_node2){ for(let t2=0; t2<child_node2.length; t2++){ if(child_node2[t2].nodeType==3){ child_node2[t2].nodeValue=child_node2[t2].nodeValue.replace(/\u00A0/g , '\u0020'); } else{ let child_node3=child_node2[t2].childNodes; if(child_node3){ for(let t3=0; t3<child_node3.length; t3++){ if(child_node3[t3].nodeType==3){ child_node3[t3].nodeValue=child_node3[t3].nodeValue.replace(/\u00A0/g , '\u0020'); }}}}}}}} let regex=new RegExp('\u00A0', 'g'); nbsp=pre_box[i].innerText.match(regex); if(nbsp !=null){ alert("❌ 変換できなかった「 」の数 : " + nbsp.length );} if(a_count !=0){ alert("❌ 置換えが出来ない「a要素」 : " + a_count); }}} function disable(){ if(target.firstChild.tagName !="IFRAME"){ target.firstChild.remove(); } // To Space (nbsp) 起動表示を削除 for(let i=0; i<pre_box.length; i++){ if(pre_box[i].firstChild.tagName=='PRE'){ pre_box[i].style.outline='none'; }} // 処理済枠の outline を削除 pre_box=[]; }// 動作の無効化 });
「BR Checker」「To Space (nbsp)」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。
●「BR Checker」は、後に「BR Checker+Outro Style ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。
●「To Space (nbsp)」は、後に「PreBox Tools ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。