「pre枠」の整形機能 「To Space」

コード表記の「pre枠」内のペースト操作で大量の「 」(no-break space)が生成されます。 この原因はインデントの半角空白が、ペースト操作時に「 」に変化するためで、これはアメブロ編集画面のエディターの仕様です。

 

下は「pre枠」にコードをペーストした例で、青背景の部分が「 」です。

 

 

「 」は記事上の表示は半角空白と同じですが、実際は半角6文字分のデータとなり、記事の文字数制限超過の原因になります。 そこで「 」を半角空白に置き換え、文字数を減らすためのツール「To Space」が必要になりました。

 

●「Ctrl + F9」を押して「BR Checker + To Space ⭐」を起動

  → 編集画面の上に赤いツールの表示が出ます。

● 「 」の置換えを行う「pre枠」を右クリック

  → ダイアログが表示され、OKを押すと整形処理が実行されます。

 

 

 

 

「p要素」の段分けと 「br」による改行

文字列中で「Enter」を押すと段落分けの指定となり「p要素」が発生します。 一方、文字列中で「Shift + Enter」を押すと「brタグ」が書き込まれます。 表示上は変わらないのですが、文書の複数段落のコピー時に、両者で結果が変わる場合があります。

 

これも普通は気にする問題ではありませんが、「br」による改行をブログ記事の標準の「段落分け」に置き換える機能が「BR Checker」です。

 

●「Ctrl + F9」で「BR Checker + To Space ⭐」を起動

 → 本文の「brタグ」の位置に「」が表示されます。

●「Ctrl + F10」を押すと、段落への自動置換が実行されます

 

 

「shell値」は処理中断があると意味を持ちますが、気にする必要はありません。

この整形機能の詳細は、以下のページの最後を参照ください。

 

 

 

 

文書の文字数カウンター 

文字数制限が問題になる場合、正確な文字数を判断できる文字数カウンターが必要になります。「BR Checker + To Space ⭐」には、文字数カウンターを搭載していますが、このカウンターはその目的で作成したものです。

 

カウント値は、標準カウンターと違い自動的には更新されません。

 

「Ctrl + F9」または「Ctrl + F10」を押した時にカウント値を更新

カウント値は半角文字数 

 

半角文字数で 約58000~60000文字が、記事文字数の上限です。 実際の上限値は、記述内容で微妙に変化しますが、それを可能な範囲で補正しています。

 

下は制限文字数のテストで、あと1文字を追加すると文字限界で保存できません。

ツールのカウンターは「約98%」、標準カウンター×2は「約120%」の表示ですから、標準カウンターはかなり大雑把です。

 

 

 

 

 「アメンバー限定公開」ボタン廃止に伴う更新

編集画面の投稿ボタンで、 「アメンバー限定公開」ボタンが廃止され、このツールがスクリプトエラーを生じました。 エラーは問題を生じないレベルでしたが、対策更新を行い抑止しました。

 

また、起動表示帯の配置コードを改善し、編集画面の幅で表示する様にしました。

 

〔追記〕2020.11.09

Firefoxで「HTML編集画面」での編集終了にコードが反応ができない問題に対処して、「mousedown」→「click」の変更をしました。 以下の「ver. 2.2f」は、この対策を更新した版です。

 

 

 

「BR Checker + To Space (nbsp) ⭐」ver. 2.2 

◎このツールは Chrome / 新Edge / Firefox の拡張機能「Tampermonkey」上で動作を確認しています。「Tampermonkey」の導入と初期設定などについては、以下を参照ください。

 

 

◎以下のコードを「Tampermonkey」の新規作成画面にコピー&ペーストして保存すると、最新版エディタの「編集画面」でこのツールを使用する事が出来ます。

 

〔コピー方法〕 右サイドバーの   マークのボタンを1度押してください。

 コード枠内の右クリック ➔ コード全体の選択 ➔ コピー操作 が可能になります。

 

 

〔 BR Checker + To Space (nbsp) ⭐ 〕 ver. 2.2f

 

// ==UserScript==
// @name         BR Checker + To Space (nbsp) ⭐
// @namespace    http://tampermonkey.net/
// @version      2.2f
// @description  Blogの書式整形ツール・文字数カウンター 統合版
// @author        Ameba blog User
// @match        https://blog.ameba.jp/ucs/entry/srventry*
// @exclude      https://blog.ameba.jp/ucs/entry/srventrylist.do*
// @grant         none
// ==/UserScript==


let retry=0;
let interval=setInterval(wait_target, 100);
function wait_target(){
    retry++;
    if(retry>10){ // リトライ制限 10回 1sec
        clearInterval(interval); }
    let target=document.getElementById('cke_1_contents'); // 監視 target
    if(target){
        clearInterval(interval);
        main(); }}


function main(){

    let marked1=0; // BR Checker 起動・非起動の指標
    let marked2=0; // To Space (nbsp) 起動・非起動の指標
    let target;
    let editor_iframe;
    let iframe_doc;
    let iframe_body;
    let 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(){
        if(document.querySelector('.l-gHeaderLeft__link a')){ // 起動を「トップページ」アイコンに表示 📛
            document.querySelector('.l-gHeaderLeft__link a').style.boxShadow='inset -14px 0 0 0 #79fbf6'; }

        let send;

        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){ // WYSIWYG表示が実行条件
            iframe_doc=editor_iframe.contentWindow.document;

            if(marked1==1){
                sign(); }
            if(marked2==1){
                active(); }

            document.addEventListener("keydown", check_key);
            iframe_doc.addEventListener("keydown", check_key);


            function check_key(event){
                let gate=-1;

                if(event.ctrlKey==true){
                    if(event.keyCode==120){
                        event.preventDefault(); send=120; gate=1; }} // ショートカット「Ctrl+F9」
                if(event.ctrlKey==true){
                    if(event.keyCode==121){
                        event.preventDefault(); send=121; gate=1; }} // ショートカット「Ctrl+F10」
                if(editor_iframe){
                    if(gate==1){
                        event.stopImmediatePropagation();
                        event.preventDefault();
                        set_task(send); }}}

            brfore_end(); }} // catch_key


    function set_task(send){
        counter();
        if(send==120){ //「Ctrl+F9」 BR Checker / To Space (nbsp) スイッチ
            if(marked1==0){
                marked1=1;
                marked2=1;
                sign();
                active(); } // BR Checker / To Space (nbsp) をON
            else if(marked1==1){
                marked1=0;
                marked2=0;
                sign_clear();
                disable(); }} // BR Checker / To Space (nbsp) をOFF
        if(send==121){ //「Ctrl+F10」BR Rewrite スイッチ
            marked1=1;
            sign_clear();
            rewrite(); }
    } // set_task



    // ********** BR Rewrite  Functions **************

    function rewrite(){
        let br_tag;
        let br_rewrite;
        let o_tag;
        let native_line;

        first_rw(go_back);

        function first_rw(){
            let wysiwyg=iframe_doc.querySelector('html');
            native_line=wysiwyg.scrollTop; // 通常表示のスクロール位置を記録

            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){
                    br_rewrite=iframe_doc.createElement("p");
                    br_rewrite.appendChild(iframe_doc.createTextNode(""));
                    br_rewrite.setAttribute("class", "brs");
                    br_tag[i].parentNode.replaceChild(br_rewrite, br_tag[i]); }}

            go_back(); }


        function go_back(){
            document.querySelector('button[data-mode="source"]').click(); // HTML表示に移動

            let interval0=setInterval(find_mirror, 10);
            function find_mirror(){
                let CodeMirror=document.querySelector('.CodeMirror');
                if(CodeMirror){
                    clearInterval(interval0);
                    document.querySelector('button[data-mode="wysiwyg"]').click();
                    go_back2(); }}}

        function go_back2(){
            let interval1=setInterval(find_iframe, 10);
            function find_iframe(){
                let editor_iframe=document.querySelector('.cke_wysiwyg_frame');
                if(editor_iframe){
                    iframe_doc=editor_iframe.contentWindow.document;
                    if(iframe_doc){
                        clearInterval(interval1);
                        setTimeout( function(){
                            second_rw(); }, 400);
                        setTimeout( function(){
                            sign(); }, 800); }}}}

        function second_rw(){
            o_tag=iframe_doc.querySelectorAll('.brs');
            if(o_tag.length >0){
                for(let i=0; i < o_tag.length; i++){
                    if(o_tag[i].nextElementSibling==o_tag[i+1] ){
                        o_tag[i].classList.remove('brs'); } // 2連 .brs 生成の場合は先頭側class名を削除
                    else{
                        o_tag[i].remove(); }}} // 単体の .brs の場合は削除(上の後方側も含む)

            let wysiwyg=iframe_doc.querySelector('html');
            wysiwyg.scrollTop=native_line; }} // 記録された通常表示のスクロール位置に移動



    // ********** BR Checker  Functions **************

    function sign(){
        let br_tag;
        let br_mark;
        let i_tag;
        let o_tag;
        let c_br=0;
        let c_br_span;
        let c_brrw=0;
        let c_brrw_span;

        let css_inn=
            '.brm { position: absolute } '+
            'i.brm:before { content: "▼"; color: red; margin-left: -2px; font-style: normal } '+
            'p.brs:before { content: "▼"; color: #008fff; margin-left: -2px; font-style: normal }';

        let style_inn=iframe_doc.createElement('style'); // iframe内の BRのデザインを指定
        style_inn.setAttribute("id", "style_inn");
        style_inn.insertAdjacentHTML('afterbegin', css_inn);
        let html=iframe_doc.getElementsByTagName('html')[0];
        if(!html.querySelector('#style_inn')){
            html.appendChild(style_inn); }

        i_tag=iframe_doc.querySelectorAll('.brm');
        if(i_tag.length >0){
            for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }} // BRマーク削除してリセット
        o_tag=iframe_doc.querySelectorAll('.brs'); // 処理時の brshell の処理残りをカウント確認
        if(o_tag.length >0){
            c_brrw=o_tag.length; }
        else{ c_brrw=0; }

        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", "brm");
                br_tag[i].parentNode.insertBefore(br_mark, br_tag[i]); }
            else{ continue; }} // BRマーク表示


        let css_ext=
            '#cke_1_contents { display: flex; flex-direction: column; } '+
            '.disp_line { display: inline-block; margin: 0 0 5px -14px; padding: 4px 0 2px; '+
            'font-size: 16px; color: #fff; background: red; align-self: center; }';

        let disp=document.createElement("span");
        disp.setAttribute("class", "disp_line");
        disp.appendChild(document.createTextNode(" ▼ BR Checker count : "));
        c_br_span=document.createElement("span");
        c_br_span.setAttribute("style", "font-weight: bold");
        c_br_span.appendChild(document.createTextNode(c_br));
        disp.appendChild(c_br_span);
        disp.appendChild(document.createTextNode(" / shell:"));
        c_brrw_span=document.createElement("span");
        c_brrw_span.setAttribute("style", "font-weight: bold; padding-right: 2em");
        c_brrw_span.appendChild(document.createTextNode(c_brrw));
        disp.appendChild(c_brrw_span);
        if(marked2==1){
            disp.appendChild(document.createTextNode("  ■ To Space (nbsp)  ")); }

        monitor.disconnect(); // MutationObserverを BR Checker 起動表示に反応させない

        let style_ext=document.createElement('style'); // disp_line のデザインを指定
        style_ext.setAttribute("id", "style_ext");
        style_ext.insertAdjacentHTML('afterbegin', css_ext);
        if(!target.querySelector('#style_ext')){
            target.appendChild(style_ext); }

        let body_width;
        iframe_body=iframe_doc.querySelector('body.cke_editable');
        if(iframe_body){
            body_width=iframe_body.style.width;
            if(body_width){
                disp.style.width=body_width; }}

        if(target.querySelector('.disp_line')){
            target.querySelector('.disp_line').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.querySelector('.disp_line')){
            target.querySelector('.disp_line').remove(); } // BR Checker 起動表示を削除

        i_tag=iframe_doc.querySelectorAll('.brm');
        if(i_tag.length >0){
            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;

        iframe_doc=editor_iframe.contentWindow.document;
        if(iframe_doc){ //「通常表示」が動作条件
            iframe_body=iframe_doc.querySelector('body.cke_editable');
            if(iframe_body){
                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');
                    let 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.length !=0){
                            let ok;
                            if(nbsp==null && pa_count.length !=0){
                                ok=confirm(" ⏬  「&nbsp」 0 個\n"+
                                           "    「a要素」 " + pa_count.length + " 個をテキストに置換えます"); }
                            else if(nbsp !=null && pa_count.length==0){
                                ok=confirm(" ⏬  「&nbsp」 " + nbsp.length + " 個を半角空白に変換します\n"+
                                           "    「a要素」 0 個"); }
                            else{
                                ok=confirm(" ⏬  「&nbsp」 " + nbsp.length + " 個を半角空白に変換します\n"+
                                           "    「a要素」 " + pa_count.length + " 個をテキストに置換えます"); }
                            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」の数 : " + nbsp.length );}
                    if(a_count !=0){
                        alert("❌ 置換えが出来ない「a要素」 : " + a_count); }}}}}


    function disable(){
        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=[]; }// 動作の無効化


    // ********** Before End Functions **************


    function brfore_end(){
        var submitButton=document.querySelectorAll('.js-submitButton');
        if(editor_iframe !=null){ //「通常表示」編集画面が実行条件
            submitButton[0].addEventListener("click", all_clear, false);
            submitButton[1].addEventListener("click", all_clear, false); }

        function all_clear(){
            let o_tag=iframe_doc.querySelectorAll('.brs'); // 処理時の brshell の処理残りをカウント確認
            if(o_tag.length >0){
                alert("⛔ BR削除処理が不完全です  BR-Shell数:" + o_tag.length +"\n\n" +
                      "   BR削除「Ctrl + F10」 を再実行してください");
                event.stopImmediatePropagation();
                event.preventDefault(); }
            else{
                let i_tag;
                editor_iframe=document.querySelector('.cke_wysiwyg_frame');
                iframe_doc=editor_iframe.contentWindow.document;
                i_tag=iframe_doc.querySelectorAll('.brm');
                if(i_tag.length >=1){
                    for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }} // マーク削除

                let pre_box;
                iframe_body=iframe_doc.querySelector('body.cke_editable');
                pre_box=iframe_body.querySelectorAll('div');
                for(let i=0; i<pre_box.length; i++){
                    if(pre_box[i].firstChild.tagName=='PRE'){
                        pre_box[i].style.outline='none'; }}}}}// 処理済枠の outline を削除


    // ********** Editor Counter **************

    function counter(){
        let html_source;
        let count_display;
        let chr_count;

        iframe_doc=editor_iframe.contentWindow.document;
        if(iframe_doc){ //「通常表示」が動作条件
            iframe_body=iframe_doc.querySelector('.cke_editable');

            iframe_body.querySelectorAll('a').forEach((link) => {
                link.removeAttribute('data-cke-saved-href'); }); //「a要素」の「data-cke-saved」補正

            iframe_body.querySelectorAll('img').forEach((link) => {
                link.removeAttribute('data-cke-saved-src'); }); //「img要素」の「data-cke-saved」補正

            html_source=iframe_body.innerHTML; // HTMLソースコードを取得
            chr_count=get_count(html_source);

            function get_count(str){
                let result=0;
                for(let i=0; i<str.length; i++){
                    let chr=str.charCodeAt(i);
                    if((chr>=0x00 && chr<0x81) || (chr===0xf8f0) ||
                       (chr>=0xff61 && chr<0xffa0) || (chr>=0xf8f1 && chr<0xf8f4)){
                        result += 1; } //半角文字の場合は1を加算
                    else{
                        result += 2; }} //それ以外の文字の場合は2を加算
                return result; }


            // 以下は、挿入要素ごとの補正
            let blockquote_tag=iframe_body.querySelectorAll('blockquote');
            let div_tag=iframe_body.querySelectorAll('div');
            let p_tag=iframe_body.querySelectorAll('p');
            let h2_tag=iframe_body.querySelectorAll('h2');
            let h3_tag=iframe_body.querySelectorAll('h3');
            let h4_tag=iframe_body.querySelectorAll('h4');
            let ul_tag=iframe_body.querySelectorAll('ul');
            let ol_tag=iframe_body.querySelectorAll('ol');
            let li_tag=iframe_body.querySelectorAll('li');
            let pre_tag=iframe_body.querySelectorAll('pre');
            let br_tag=iframe_body.querySelectorAll('br');
            let iframe_tag=iframe_body.querySelectorAll('iframe');
            let table_tag=iframe_body.querySelectorAll('table');
            let tr_tag=iframe_body.querySelectorAll('tr');
            let td_tag=iframe_body.querySelectorAll('td');
            let style_tag=iframe_body.querySelectorAll('style');

            let correct=
                6*blockquote_tag.length +
                2*div_tag.length +
                2*p_tag.length +
                2*h2_tag.length +
                2*h3_tag.length +
                2*h4_tag.length +
                6*ul_tag.length +
                6*ol_tag.length +
                3*li_tag.length +
                6*pre_tag.length +
                2*br_tag.length +
                2*iframe_tag.length +
                12*table_tag.length +
                8*tr_tag.length +
                5*td_tag.length +
                6*style_tag.length;


            let wordcount=document.querySelector('.cke_wordcount');
            wordcount.style.margin="9px 20px 0 0";
            wordcount.style.padding="0 10px";
            let path_item=document.querySelector('.cke_path_item');
            path_item.style.font="normal 14px Meiryo";
            path_item.style.color="#000";
            let real_count=document.createElement("span");
            real_count.setAttribute("class", "real_count");
            real_count.style.font="normal 14px Meiryo";
            real_count.style.color="#000";
            if(wordcount.querySelector('.real_count')){
                wordcount.querySelector('.real_count').remove(); }
            wordcount.appendChild(real_count);

            real_count.innerHTML=' 半角: ' + (chr_count+correct); }}

} // main()

 

 

 

「BR Checker」「To Space (nbsp)」最新版について 

旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。

 

●「BR Checker」は、後に「BR Checker+Outro Style ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。

 

 

●「To Space (nbsp)」は、後に「PreBox Tools ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。