人様に勧める自信がだんだん無くなるツール

アメブロ公式スキンの基本フォントサイズは、時代に合わせて拡大されて来ました。

2004年当初の「レトロタイプスキン」は「12px」でしたが、これは現在も使っている方が居られます。 その後に「旧タイプスキン」の「14px」、更に「新タイプスキン」で「16px」になりました。 この拡大は、ユーザーが使用するPCモニターの高精細化に合わせて、実表示の文字サイズを大きくする必要があったからです。(高精細モニターではフォントはより小さく表示されるため)

 

「RemPage」ツール(旧名 Remember My Page)は、文字が小さく読みづらいブログを、自動的に読み易く拡大して表示する目的で制作したものです。(ブラウザの拡大機能はドメインごとに拡大率を記憶できますが、アメーバ内は全て「ameblo.jp」のドメインで、ブログ個別の拡大率を記憶できません。)

 

このツールは、先ずページの文字サイズを判定して、自動で読み易い拡大をします。 しかし、独自のアレンジを施したページでは、自動判定が通用しない場合があるので、その場合はこちらで拡大率を設定して記憶させる事が出来ます。

 

 

問題点が多い 

この機能、完璧に動作するならとても良い機能ですが、残念な事に幾つも問題を抱えています。 最初に直面したのは、画面拡大機能で使い易い「zoom」プロパティが、 Chromeは対応するが Firefoxでは使えない事です。 これで、Firefox版は現在も実験版の域を出ません。 ページ表示上の問題があり、実用的とはとても言えなません。

 

Chrome版の方は一応は使えるますが、長く使っていると「なんだかな」の問題に時々気付きます。 今回は、やたら「スクリプトエラー」を吐いていました。 実害の程度は不明ですが、エラー元を調べると前バージョンの更新が原因でした。

 

144行のコードで、ページに拡大率を設定する「styleタグ」を、「remove」で削除しては生成する「更新」タイプにしています。

 

if(document.querySelector('#chzoom')){
    document.querySelector('#chzoom').remove(); }
document.documentElement.insertAdjacentHTML('beforeend', style);

 

このコードはページ読込みで各種のパーツが生成される度に、不必要に「styleタグ」を更新し、以下のエラーが多発します。 内容はページがどんどん変化しているので「MutationObserver」は処理を飛ばしますというもの。 

 

The page keeps generating mutations. Skip the event.
    at MutationObserver.

 

DevToolsのエラー表示は下の様に、このエラーだらけです。 飛ばされたのはこのツールの拡大処理で、不要な拡大処理を強いていた結果です。

 

 

これがページ表示の足を引っ張る程度は不明ですが、errorは無いにこした事はありません。

 

で、コードを修正して、ページ読込み時は「styleタグ」は1回生成するだけに改めました。「更新型」のコードは、拡大率の設定の際に必要で、ユーザー設定時のみ「更新型」となる様に改めました。

 

これで今回のエラー増産は、あっさり改善しています。

 

 

勧められるツールなのか? 

正直な所、このツールは色々と問題を引き起こし易いと感じています。「zoom」プロパティを使うページの拡大表示は、ブラウザの「ページ拡大表示」とは異なります。 例えば、要素の位置(x, y)を取得する場合、「zoom」表下では取得値に補正が必要ですが、ブラウザ拡大の場合は不要です。

 

そういった問題がいつも生じるわけでは有りませんが、「zoom」「transform」などのどれを使っても、ページ全体の拡大・縮小表示は厄介な作業と感じます。 もし、ブラウザ拡大機能を拡張機能のレベルで利用できたら、もっと万能のツールを作れると思います。 しかし、私は現在のところは、「Tampermonkey」を使う、ユーザースクリプトのツール制作を基本にしていますので。

 

という事で、「RemPage For Chrome」を利用する場合は、不完全な部分があるかもしれないツールと考えて扱ってください。

 

 

 

「RemPage For Chrome」の扱い方 

「RemPage For Chrome」の扱い方は以下のページに纏めています。

 

 

 

 

「RemPage For Chrome」を使うには

このツールは、Chrome / Edge で動作を確認しています。

(Firefoxでは正常な動作をしないので、For Firefox版を使用してください)

 

❶「Tampermonkey」を導入します

◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。

既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。 

拡張機能の導入については、以下のページに簡単な説明があるので参照ください。

 

 

❷「Tampermonkey」にスクリプトを登録します

◎「Tampermonkey」の「」マークの「新規スクリプト」タブを開きます。

 

 

 

◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。

 

〔コピー方法〕 軽量シンプルなツール「PreBox Button   」を使うと

  コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」

  の操作で、掲載コードのコピーが可能になります。

 

◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。

 

 

〔 RemPage For Chrome 〕ver. 3.4

 

// ==UserScript==
// @name         RemPage For Chrome
// @namespace    http://tampermonkey.net/
// @version      3.4
// @description  ブログページ拡大表示をブログ単位で記録・固定(zoom使用)
// @author       Ameba Blog User
// @match        https://ameblo.jp/*
// @exclude      https://ameblo.jp/*/image*
// @run-at        document-start
// @noframes
// @grant        none
// ==/UserScript==


let remember_data={}; // ブログ登録データ
let remember_id=[];
let remember_zoom=[];
let z_button;
let user_id;
let zoom_value;
let zoom_auto=0;
let edit_mode=0;


reg_set();

function reg_set(){
    let read_json=localStorage.getItem('remember_zoom'); // ローカルストレージ 保存名
    remember_data=JSON.parse(read_json);
    if(remember_data==null){
        remember_data=[['user', 'zoom']]; } // ブログ記事データ

    remember_id=[]; // 配列をリセット
    remember_zoom=[]; // 配列をリセット
    for(let k=0; k<remember_data.length; k++){
        remember_id[k]=remember_data[k][0];
        remember_zoom[k]=remember_data[k][1]; }}



let retry=0;
let interval=setInterval(wait_target, 1);
function wait_target(){
    retry++;
    if(retry>100){ // リトライ制限 100回 0.1secまで
        clearInterval(interval); }
    let target=document.body; // 監視 target
    if(target){
        clearInterval(interval);
        zoom_startup(); }}



function zoom_startup(){
    do_zoom();

    setTimeout(()=>{
        center(); }, 100);

    let target0=document.querySelector('head'); // 監視 target
    let monitor0=new MutationObserver(do_zoom);
    monitor0.observe(target0, { childList: true }); // 監視開始

    let target1=document.querySelector('#announcer'); // 監視 target
    if(target1){
        let monitor1=new MutationObserver(path_change);
        monitor1.observe(target1, { childList: true }); } // 監視開始

} // zoom_startup()



function do_zoom(){
    zoom_reset();
    center();
    backup_set(); }



function zoom_reset(){
    user_id=document.location.pathname.split("/")[1]; // ユーザーIDを取得

    if(remember_id.indexOf(user_id)!=-1){
        zoom_value=remember_zoom[remember_id.indexOf(user_id)];
        if(zoom_value==1){
            zoom_auto=3; } // 登録有 100% zoom を適用
        else { zoom_auto=2; }} // 登録有 manual zoom を適用
    else{
        let entryBody=document.querySelector('#entryBody');
        if(entryBody){
            if(getComputedStyle(entryBody, null).fontSize=='14px'){ // 未登録14px型
                zoom_auto=1; // 未登録 auto zoom を適用
                zoom_value=1.14; }
            else{
                zoom_auto=0; // 未登録 zoom 無し
                zoom_value=1; }}
        else{
            zoom_auto=0; // 未登録 zoom 無し
            zoom_value=1; }} // entryBodyが取得出来ない場合

    ch_zoom(zoom_value); // Chrome場合 拡大実行

} // zoom_reset() zoom値の選択と拡大実行



function ch_zoom(val){ // Chrome用のページ拡大コードを生成
    let style;

    if(val!=1){
        style=
            '<style id=chzoom>body { zoom: '+ val +' } '+
            '#ambHeader, footer, .ReactModalPortal { zoom: '+ 1/val +' } '+
            '._20TD20u0, ._HQiG1eoZ { display: none } '; }
    else{
        style=
            '<style id=chzoom>'+
            '._20TD20u0, ._HQiG1eoZ { display: none } '; }

    style+=
        '#ambHeader img[alt="Ameba"] { filter: contrast(6) hue-rotate(90deg); ';

    if(zoom_auto==0){ // 未登録 zoom 無し
        style+=
            'box-shadow: none; padding-left: 0 }</style>'; }
    else if(zoom_auto==1){ // 未登録 auto zoom
        style+=
            'box-shadow: -10px 0 0 #e98200; padding-left: 5px }</style>'; }
    else if(zoom_auto==2){ // 登録有 manual zoom
        style+=
            'box-shadow: -10px 0 0 #7aff00; padding-left: 5px }</style>'; }
    else if(zoom_auto==3){ // 登録有 100% zoom
        style+=
            'box-shadow: -10px 0 0 #9c27b0; padding-left: 5px }</style>'; }

    if(!document.querySelector('#chzoom')){
        document.documentElement.insertAdjacentHTML('beforeend', style); }

} // ch_zoom()



function center(){ // 記事本文を中央に配置
    let ht=document.documentElement.scrollTop;
    let main_a=document.querySelector('#main');
    if(main_a){
        let cr_left=main_a.getBoundingClientRect().left;
        let cr_width=main_a.getBoundingClientRect().width;
        let center_p=cr_left + cr_width/2;
        let v_center=center_p*zoom_value;
        let window_w=document.documentElement.clientWidth;
        let scrooll_step=v_center - window_w/2
        if(ht<10){ // 縦スクロール後は動作しない
            if(scrooll_step>10 || scrooll_step<-10){
                scrollBy(scrooll_step, 0 ); }}}}



function backup_set(){
    z_button=document.querySelector('#ambHeader img[alt="Ameba"]');
    if(z_button){
        z_button.onclick=function(event){ // Amebaアイコンのクリック
            event.preventDefault(); // クリックしてもホームに飛ばさない
            backup(); }}}



function backup(){
    edit_mode=1;
    disp_panel();

    let z_panel=document.querySelector('#z_panel');
    if(z_panel){

        let z_close=z_panel.querySelector('#z_close');
        z_close.onclick=function(){
            edit_mode=0;
            zoom_reset();
            z_panel.remove(); }


        let z_num=z_panel.querySelector('#z_num');
        z_num.value=Math.round(zoom_value*100);
        document.onwheel=function(event){ // マスウホイールで設定
            event.stopPropagation();
            event.stopImmediatePropagation();
            if(event.deltaY<0 && edit_mode==1){
                z_num.stepUp(); }
            else if(event.deltaY>0 && edit_mode==1){
                z_num.stepDown(); }
            if(edit_mode==1){
                remove_style();
                ch_zoom(z_num.value/100); }}


        z_num.onchange=function(event){
            event.stopPropagation();
            event.stopImmediatePropagation();
            if(edit_mode==1){
                if(z_num.value<80){
                    z_num.value=80; }
                else if(z_num.value>200){
                    z_num.value=200; }
                remove_style();
                ch_zoom(z_num.value/100); }}


        let z_set=z_panel.querySelector('#z_set');
        z_set.onclick=function(){
            let zoom_set=z_num.value/100; // 登録するzoom倍率

            if(remember_id.indexOf(user_id)==-1){ // 未登録のIDの場合
                remember_data.push([user_id, zoom_set]); // 等倍も登録する
                let write_json=JSON.stringify(remember_data);
                localStorage.setItem('remember_zoom', write_json); }
            else if(remember_id.indexOf(user_id)!=-1){ // 既登録のIDの場合
                remember_data.splice(remember_id.indexOf(user_id), 1); // 一旦は登録削除
                remember_data.push([user_id, zoom_set]); // 登録値を更新して追加
                let write_json=JSON.stringify(remember_data);
                localStorage.setItem('remember_zoom', write_json); }

            reg_set();
            edit_mode=0;
            remove_style();
            do_zoom();
            z_panel.remove(); }


        let z_reset=z_panel.querySelector('#z_reset');
        z_reset.onclick=function(){
            if(remember_id.indexOf(user_id)==-1){ ; } // 未登録のIDの場合
            else if(remember_id.indexOf(user_id)!=-1){ // 既登録のIDの場合
                remember_data.splice(remember_id.indexOf(user_id), 1); // Resetは登録削除
                let write_json=JSON.stringify(remember_data);
                localStorage.setItem('remember_zoom', write_json); }

            reg_set();
            edit_mode=0;
            remove_style();
            do_zoom();
            z_panel.remove(); }


        let z_exp=z_panel.querySelector('#z_exp');
        z_exp.onclick=function(){
            let write_json=JSON.stringify(remember_data);
            let blob=new Blob([write_json], {type: 'application/json'});
            try{
                let a_elem=document.createElement('a');
                a_elem.href=URL.createObjectURL(blob);
                document.body.appendChild(a_elem);
                a_elem.download='remember_zoom.json'; // 保存ファイル名
                a_elem.click();
                URL.revokeObjectURL(a_elem.href);
                alert("✅  ファイルを保存しました\n"+
                      "  ダウンロードフォルダーを確認してください"); }
            catch(e){
                alert("❌ ファイル保存時にエラーが発生しました\n"+
                      "  ダウンロードフォルダーを確認してください"); }}


        let z_imp=z_panel.querySelector('#z_imp');
        let z_file=z_panel.querySelector('#z_file');
        z_imp.onclick=function(){
            z_file.click(); }

        z_file.addEventListener("change" , function(){
            if(!(z_file.value)) return; // ファイルが選択されない場合
            let file_list=z_file.files;
            if(!file_list) return; // ファイルリストが選択されない場合
            let file=file_list[0];
            if(!file) return; // ファイルが無い場合

            let file_reader=new FileReader();
            file_reader.readAsText(file);
            file_reader.onload=function(){
                if(file_reader.result.slice(0, 15)=='[["user","zoom"'){ // ファイルデータの確認
                    remember_data=JSON.parse(file_reader.result); // 読出してストレージを上書きする
                    let write_json=JSON.stringify(remember_data);
                    localStorage.setItem('remember_zoom', write_json); // ローカルストレージ 保存名
                    reg_set();
                    zoom_reset(); // 画面の拡大表示をリセット
                    alert("✅ 拡大率の登録データを読込みました\n"+
                          "   読込んだファイル名: " + file.name); }
                else{
                    alert("❌ Remember My Page の Exportファイルではありません\n"+
                          "   Importファイルは 「remember_zoom ... 」の名前です"); }}});



        let z_mag=z_panel.querySelector('#z_mag');
        let chrome_value=Math.round(((window.outerWidth - 16) / window.innerWidth)*100);
        if(chrome_value!=100){
            z_mag.innerHTML='Basic ' + chrome_value + '%';

            z_panel.style.zIndex='2';
            setTimeout(()=>{
                z_mag.style.opacity='0';
            }, 3000);
            setTimeout(()=>{
                z_mag.style.display='none';
                z_panel.style.zIndex='0';
            }, 6000); }
        else{
            z_mag.style.display='none'; }

    } // if(z_panel)
} // backup()



function remove_style(){
    if(document.querySelector('#chzoom')){
        document.querySelector('#chzoom').remove(); }}



function disp_panel(){
    let z_div=
        '<div id="z_panel">'+
        '<input id="z_close" type="submit" value="×">'+
        '<input id="z_num" type="number" step="5" min="80" max="200">'+
        '<input id="z_set" type="submit" value="Set">'+
        '<input id="z_reset" type="submit" value="Reset">'+
        '<input id="z_exp" type="submit" value="Export">'+
        '<input id="z_imp" type="submit" value="Import">'+
        '<input id="z_file" type="file">'+
        '<span id="z_mag"></span>'+
        '<style>'+
        'html { overflow: hidden; } '+
        '#z_panel { position: absolute; top: 7px; left: 0; z-index: 0; '+
        'color: #333; background: #fff; padding: 5px 0 5px 15px; width: 400px; } '+
        '#z_panel ::-webkit-inner-spin-button { -webkit-appearance: none; } '+
        '#z_panel > * { font: normal 15px Arial; padding: 2px; height: 24px; } '+
        '#z_close { font-weight: bold; width: 14px; padding: 2px 0; margin-right: 15px; } '+
        '#z_num { width: 36px; padding: 1.5px 0 0 8px; height: 18.8px; '+
        'box-sizing: content-box; } '+
        '#z_set { margin-left: 4px; } '+
        '#z_reset { margin-left: 4px; } '+
        '#z_exp { margin: 0 4px 0 15px; } '+
        '#z_imp { margin-right: 15px; } '+
        '#z_file { display: none; } '+
        '#z_mag { color: #fff; background: #0288d1; padding: 3px 4px; '+
        'border: thin solid #888; border-radius: 3px; transition: 2s; } '+
        '</style></div>';

    if(!document.querySelector('#z_panel')){
        document.querySelector('#ambHeader div:first-child')
            .insertAdjacentHTML('beforeend', z_div); }

} // disp_panel()



function path_change(){ // zoomの設定中にページ変更をした場合の error抑止
    if(edit_mode==1){
        edit_mode=0;
        let z_panel=document.querySelector('#z_panel');
        if(z_panel){
            z_panel.remove(); }}

} // path_change()


 

〔追記〕2023.04

「secret.ameba.jp」のサブドメイン廃止に伴い、上記コードを修正しました。

ver. 3.3 ➔ ver. 3.4 に更新しています。

 

 

 

「RemPage For Chrome」最新版について 

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

 

●「RemPage For Chrome」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。