「カラー値入力枠」の判定コード

 「Draw The Line」で、ここまで利用して来たカラー値入力枠は、「Blog Table」の入力枠の再利用です。 その詳細は以下にあります。

 

  アメブロ専用 table表作成ツール「Blog Table ⭐」(2) 

 

判定コードは以下のシンプルかつ有効なもので、ネットの各所で紹介されている正規表現検索を使ったものです。 これは「#+16進3桁」又は「#+16進6桁」に当てはまれば「OK」という判定方法です。

 

function test_color(color){
    return color.match(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)!==null; }

 

下は、この判定コードを実際に使用する例です。

 

if(test_color(color)){  ~~~ color の値が有効な場合に実行~~~   }

 

この判定コードは、カラー名の指定「red」、RGB表記「rgb(255,0,0)」等の入力に対応しておらず、「NG」判定をします。 実際に作ったカラー値入力枠は、CSS表記で有効なものは、全て「OK」ですが、判定は「NG」になるものがあるわけです。

 

 

「Draw The Line」では「透過度」の設定機能を組み込んでいて、「#+16進4桁」や「#+16進8桁」のカラー値も頻繁に使いますが、これも上記の判定コードは「NG」になります。 今回、この判定の問題を改善する事にしました。

 

 

 

 ブラウザ自体が表示出来る事を判定に使う

上図の入力枠の右端には、入力値のカラーを実際に表示していますが、判定が「NG」でも、実際に有効なカラー値なら色が表示されます。 これを見ていると、逆にブラウザが「色」を表示出来れば、それを「OK」と判定すれば良いと思い着きました。

 

色が表示されたらそれが判定になるのですが、判定は以下の目的で必要です。

 ▶ 有効でないカラーコード入力をユーザーに明瞭に示す

 ▶ とても淡い色の場合に、色表示は有効・無効が見分け難い

 ▶ 入力を登録する機能を実装した場合に、無効コードを排除する

 

ネットを調べると同着想の判定コードがありましたが、ここでは独自に作りました。

 

➀ 判定用の「div要素」を生成し、テストするカラー値をその「color」プロパティに設定します。 つまり「div要素」内の文字色を指定しますが、文字が無い「空」の「div要素」でも実際は動作します。

 

➁「.getComputedStyle(element).color」のメソッドを使い、要素(element)に実際に反映した文字色を調べます。 これはブラウザが実際に表示する色を調べるメソッドです。

 

例えば、要素に「red」を指定した場合は、メソッドの戻り値は「rgb(255, 0, 0)」で、「transparent」を入力すると「rgba(0, 0, 0, 0)」の戻り値になります。 このメソッドの戻り値を調べ、反映すれば有効なカラー値で「OK」と判定できます。

 

③ 判定コードの例外的な場合を処理します。 これは結構手間のかかる所で、判定用途によって厳密に例外を処理するか、適当に済ますかが変わって来ます。

 

 

判定コード 

以下が今回作成した判定コードです。

 

function test_colorE(color){
    let test=document.querySelector('#test'); // ❶
    test.style.color='#000001'; // ❷
    if(color!=''){ // 引数の入力がない場合は例外処理 ❸ 
        test.style.color=color; } // ❹

    let colorR=window.getComputedStyle(test).color; // ❺
    if(colorR){ // ❻
        if(colorR!='rgb(0, 0, 1)'){ // ❼
            alert(colorR + '  🟢 適正コード');
            return true; }
        else{ // ❽
            if(color=='rgb(0, 0, 1)' || color=='#000001' || color=='#000001ff'){ // ❾
                alert(colorR + '  🟢 適正コード');
                return true; }
            else{ // ❿
                alert(colorR +'  🔴 不適なカラーコード');
                return false; }}}
    else{ // ⓫
        alert(colorR + '  🔴 メソッド戻り値無し'); // 実際はこの状態にならない
        return false; }}

 

 

❶「#test」という「div要素」がテスト用の「div要素」です。

 

❷ 生成した「#test」に「#000001」の文字色指定を指定しています。 このカラー値は「#000000」等のありふれた指定値でなく、意図しないと設定されない値を選んでいます。 この値が変更されるか否かで、有効なカラー値かどうかを判定します。

 

❹ この関数の引数の テストするカラー値「color」を「#test」に設定しています。

 

❺「getComputedStyle(test).color」で「#test」に実際に反映した「文字色」を調べ、この戻り値を「colorR」に代入しています。

 

以下、この「colorR」の値を判定します。

 

先ず、「getComputedStyle()」の戻り値の有無で ❻ ⓫ に分岐します。 ほぼ全ての場合に戻り値がありますが、万一の用心で ⓫の分岐を用意しています。

 

次の判定は ❼ ❽で、「colorR」が「rgb(0, 0, 1)」かどうかを判定します。 

 

テストしたカラー値が無効なら、文字色は ❷で指定した「#000001」のままとなります。「getComputedStyle()」は「RGB表記」の戻り値を返すので、「#000001」=「rgb(0, 0, 1)」が戻り値になります。

 

カラー値が有効なら文字色に適用され「getComputedStyle()」は 「rgb(0, 0, 1)」と異なる値を返すので、❼の適正コードの判定になります。

 

❽ 戻り値がrgb(0, 0, 1)」の場合は、例外の場合が出て来ます。 

 

例外は、テストした値が「rgb(0, 0, 1)」の場合で、この場合は有効なカラー値と判定する必要があります。「getComputedStyle()」の戻り値「rgb(0, 0, 1)」になるカラー値は、幾つか考えられますが、その場合を ❾で有効に判定しています。

 

「#000001」「#000001ff」は確実ですが、「RGB表記」の「rgb(0, 0, 1)」は、実はこれだけではありません。 例えば「rgb(0,0,1)」も「rgb( 0,0,1 )」も半角空白は無関係で有効になり、戻り値は「rgb(0, 0, 1)」になります。

 

更に「rgb(0, 0, 1, 22)」等の誤った入力も、勝手に修正して「rgb(0, 0, 1)」を戻します。 これを拾い出して例外に加えて行くと「きりがない」ので、「RGB表記」に関しては「rgb(0, 0, 1)」だけを拾う事にしました。

 

❿ 最後に残った ❼ ❽ 以外の場合が「不適なカラーコード」の判定です。

 

更に例外処理が必要でした。 それが ❸の「引数の入力がない場合」です。

 

「カラー入力枠」のカラー値を判定する際、何も入力されていない場合があります。 この場合を ❸で除外しないと、「#test」にテスト値に「無」の入力をするので、判定用の ❷の入力がリセットされます。 その結果「#test」に親要素の文字色が継承され、それを「getComputedStyle()」が取得して判定不能になります。

 

この例外は、❸で無入力の場合を除外して、めでたく ❿に判定できました。

 

 

 

テストツール 

以上の「判定コード」の実際の動作を確認する、テストツールを作りました。 ベースは「Draw The Line」ですが、カラー値の入力と、有効なカラー値の判定と、カラー値のローカルストレージへの記録だけの機能で、実用性はありません。 

 

●「Ctrl + F3」で、ON / OFF します。 下のコントロールが表示されます。

 

 

●「アラート非表示」「アラート表示」が選択できます。「getComputedStyle()」の戻り値を確認するアラートですが、普通は「非表示」にしないと、一文字入力ごとに判定表示が出て、大変に扱い難くなります。

 

●「カラー値入力枠」は「Ctrl + 左クリック」で、カラーパレット入力が出来ます。

 

●「カラー値入力枠」に手入力でカラー値を書き込むと、1文字ごとにカラー値の判定が行われます。 無効なカラー値の場合は「カラー値入力枠」の外に「黒枠」を表示します。 有効なカラー値の場合は、入力枠の右隅にそのカラーを表示します。

 

この判定に、上記の判定コードが動作しています。 従来は「red」「Green」などのカラー名の入力で「黒枠」が出ましたが、今回は有効なカラー値と判定されます。 また、RGB表記の入力も可能で、判定が正しく動作します。

 

● 右隅はテスト用の「div要素」の「#test」です。 判り易い様に敢えて「◪」の絵文字を入れて、文字色の設定の状態を表示しています。 判定コードは、実際にこの文字の色を取得して判定しています。 ただし、この判定コードの実装に絵文字は不要で、「#test」を全く非表示に出来ます。

 

● 設定したカラー値はローカルストレージに保存しているので、ツールをON/OFFしても、最後に設定した値が再現されます。

 

●「透過度」の設定機能は、「#+16進3桁」「#+16進6桁」「#+16進8桁」の入力値の場合のみ動作します。 入力枠に、カラー名やRGB表記の値が入力されている時は、この機能は動作しません。 また「透過度」変更中は、入力判定は動作しません。

 

 

 

テストツールのコード 

このコードは Chrome / 新Edge / Firefox の「Tampermonkey」上で動作します。 

 

このコードを実行して試すには、各ブラウザの「Tampermonkey」の「新規スクリプト」の編集画面で、最初から登録されているテンプレートを削除して完全に空白にした上で、以下のコードをコピー&ペーストして「保存」してください。

 

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

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

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

 

 

〔 Color Code Test ◪ 〕ver. 0.1

 

// ==UserScript==
// @name         Color Code Test ◪
// @namespace  http://tampermonkey.net/
// @version      0.1
// @description  カラーコードの有効・無効を判定するツール
// @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 editor_iframe;
    let iframe_doc;
    let selection;
    let range;
    let task=0; // アンダーライン・マーカー線・消し線・終了
    let add_padd; //「MS Pゴシック」のために padding-bottom追加フラグ

    let read_json;
    let setting; // 入力枠の設定とユーザー設定値登録

    let ua=0; // Chromeの場合のフラグ
    let agent=window.navigator.userAgent.toLowerCase();
    if(agent.indexOf('firefox') > -1){ ua=1; } // Firefoxの場合のフラグ



    read_json=localStorage.getItem('Draw_Line'); // ローカルストレージ 保存名
    setting=JSON.parse(read_json);
    if(setting==null){
        setting=[['DrawTheLine','0','0','0'],['1','1.23','#333','0'],['1','1.23','#333','0'],
                 ['0.6','0.7','#ccc','0'],['1','0.66','#333','0'],['1','0.54','#333','0']]; }
    let write_json=JSON.stringify(setting);
    localStorage.setItem('Draw_Line', write_json); // ローカルストレージ 保存


    let 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('.cke_wysiwyg_frame') !=null){ //「通常表示」から実行開始
            editor_iframe=document.querySelector('.cke_wysiwyg_frame');
            iframe_doc=editor_iframe.contentWindow.document;
            selection=iframe_doc.getSelection();

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

            function check_key(event){
                let gate=-1;
                if(event.ctrlKey==true){
                    if(event.keyCode==114){
                        event.preventDefault(); gate=1; }
                    if(gate==1){
                        event.stopImmediatePropagation();
                        do_task(); }}}

            function do_task(){
                if(task==0){
                    task=1;
                    panel_disp();
                    set_panel();
                    draw_line(); }
                else if(task==1 || task==2){
                    task=0;
                    panel_remove(); }}
        }} // catch_key()



    function draw_line(){
        show_color();
        pick_color();

        let l_type=document.querySelector('#l_type');
        let single=document.querySelector('#single');
        let double=document.querySelector('#double');
        let l_trance=document.querySelector('#l_trance');
        let l_color=document.querySelector('#l_color');
        let ms=document.querySelector('#ms');

        single.checked=true;
        l_color.value=setting[1][2];
        test_colorE(l_color.value); // test_colorEを実行 ⏹

        double.onclick=function(){
            double.checked=true;
            if(task==1){
                task=2; }}

        single.onclick=function(){
            single.checked=true;
            if(task==2){
                task=1; }}


        l_trance.addEventListener('input', function(event){
            event.preventDefault();
            let able=0;
            if(test_color(l_color.value)){ // 3桁 6桁の有効コード
                able=1; }
            if(l_color.value.length==9){ // 8桁で変更可能な場合
                if(test_color(l_color.value.slice(0, -2))){
                    able=1; }}
            if(able==1){
                trance();
                l_color.style.boxShadow='inset -20px 0 ' + l_color.value; }
            else{
                l_trance.value=1; } // 変換不能なコードは「1」を変更できない

            setting[1][2]=l_color.value;
            let write_json=JSON.stringify(setting);
            localStorage.setItem('Draw_Line', write_json); }); // ローカルストレージ 保存

    } // draw_line()



    function set_panel(){
        let l_type=document.querySelector('#l_type');
        let l_trance=document.querySelector('#l_trance');
        let l_color=document.querySelector('#l_color');

        l_type.value="カラー値チェック";

        l_trance.value='1'; // 初期値
        l_trance.setAttribute('min', '0.1');
        l_trance.setAttribute('max', '1');
        l_trance.setAttribute('step', '0.1');

        l_color.value=setting[1][2]; }



    function panel_disp(){
        let panel=document.createElement('div');
        panel.setAttribute('id', 'l_panel');

        panel.innerHTML=
            '<input id="l_type" type="submit">'+
            '<div id="type_wrap">'+
            '<div id="type_1">'+
            '<input id="single" type="radio" name="s_d"><span class="l_label">アラート非表示</span>'+
            '<input id="double" type="radio" name="s_d"><span class="l_label">アラート表示</span>'+
            '</div>'+
            '<div id="type_2">'+
            '<span class="l_label">透過度</span>'+
            '<div class="wtr"><input id="l_trance" type="number"></div>'+
            '</div>'+
            '</div>'+
            '<span class="l_label">線色</span>'+
            '<input id="l_color" type="text" value="#333" autocomplete="off">'+
            '<div id="test">◪</div>';
//        '<div id="test"></div>';


        let css=
            '#l_panel { position: fixed; top: 15px; left: calc(50% - 490px); padding: 6px 15px; '+
            'font-size: 14px; border: 1px solid #ccc; border-radius: 4px; background: #eff5f6; z-index: 10; }'+
            '#type_wrap { display: inline-block; text-align: right; width: 370px; }'+
            '#type_1 { display: inline-block; margin-right: 20px; } #type_2 { display: inline-block; }'+
            '#l_panel input { position: relative; margin-right: 10px; padding-top: 2px; }'+
            '#l_panel input:hover { z-index: 1; }'+
            '#l_panel input[type="radio"] { margin: 0 2px 0 8px; vertical-align: -2px; box-shadow: none; }'+
            '.l_label { margin: 0 4px 0 0; }'+
            '#l_type { width: 130px; margin-right: 4px !important; }'+
            '.wtr { position: relative; display: inline-block; }'+
            '#l_trance { width: 40px; text-align: center; padding: 2px 0 0 4px; }'+
            '#l_color { width: 90px; padding: 2px 24px 0 6px; border: thin solid #aaa; height: 23px; }'+
            '#test { display: inline-block; padding: 4px 7px 3px; border: thin solid #aaa; background: #fff; }'+
//            '#test { display: inline-block; }'+
            '#cke_42 { top: 60px !important; left: calc( 50% - 45px) !important; }';

        if(ua==1){
            css=css +
                '#l_trance, #l_color { height: 24px; border: thin solid #aaa; }'+
                '.wtr::after { content: " "; position: absolute; right: 11px; top: 5px; '+
                'background: #fff; width: 1.2em; }'; }

        let style=document.createElement('style');
        style.innerHTML=css;
        panel.appendChild(style);

        let l_panel=document.querySelector('#l_panel');
        if(!l_panel){
            document.querySelector('.l-body').appendChild(panel); }} // panel_disp()



    function panel_remove(){
        let l_panel=document.querySelector('#l_panel');
        l_panel.remove(); }



    function test_color(color){
        return color.match(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)!==null; }



    function test_colorE(color){
        let test=document.querySelector('#test');
        test.style.color='#000001';
        if(color!=''){
            test.style.color=color; } // 引数の入力がない場合
        let colorR=window.getComputedStyle(test).color;
        if(colorR){
            if(colorR!='rgb(0, 0, 1)'){
                if(task==2){
                    alert(colorR + '  🟢 適正コード'); }
                return true; }
            else{
                if(color=='rgb(0, 0, 1)' || color=='#000001' || color=='#000001ff'){
                    if(task==2){
                        alert(colorR + '  🟢 適正コード'); }
                    return true; }
                else{
                    if(task==2){
                        alert(colorR +'  🔴 不適なカラーコード'); }
                    return false; }}}
        else{
            if(task==2){
                alert(colorR + '  🔴 メソッド戻り値無し'); } // 実際はこの状態にならない
            return false; }}


    function show_color(){
        let l_color=document.querySelector('#l_color');
        l_color.style.boxShadow='inset -20px 0 ' + l_color.value; }



    function pick_color(){
        let set_color;
        let color_input_selector;
        let color_label;
        let icon_button;

        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        iframe_doc=editor_iframe.contentWindow.document;
        selection=iframe_doc.getSelection();

        if(ua==0){
            color_label=document.querySelector('#cke_16_label');
            icon_button=document.querySelector('#cke_17'); }
        else if(ua==1){
            color_label=document.querySelector('#cke_15_label');
            icon_button=document.querySelector('#cke_16'); }

        let target_p=color_label; // 監視 アイコンのカラーラベル
        let monitor_p=new MutationObserver( get_copy );

        let l_color=document.querySelector('#l_color');


        l_color.onclick=function(event){
            if(event.ctrlKey==true){
                event.preventDefault();
                icon_button.click();
                selection.removeAllRanges(); // 反転選択がある場合に背景指定を防止する
                monitor_p.observe(target_p, {attributes: true}); }} // アイコンカラー取得開始

        l_color.addEventListener('input', function(event){
            event.preventDefault();
            let l_trance=document.querySelector('#l_trance');
            if(l_trance){
                l_trance.value=1; } // 透過度をリセットする

            if(test_colorE(l_color.value)){ // test_colorEを実行 ⏹
                l_color.style.boxShadow='inset -20px 0 ' + l_color.value; }
            else{
                if(l_color.value==''){
                    l_color.style.boxShadow='inset 0 0 0 1px black'; }
                else{
                    l_color.style.boxShadow='inset 0 0 0 1px black'; // 担保コード
                    l_color.style.boxShadow=
                        'inset 0 0 0 1px black, inset -20px 0 ' + l_color.value; }}

            setting[1][2]=l_color.value;
            let write_json=JSON.stringify(setting);
            localStorage.setItem('Draw_Line', write_json); }); // ローカルストレージ 保存


        document.addEventListener('mousedown', function(){
            monitor_p.disconnect(); }); // アイコンカラー取得終了

        if(document.querySelector('.cke_wysiwyg_frame') !=null){
            let editor_iframe=document.querySelector('.cke_wysiwyg_frame');
            let iframe_doc=editor_iframe.contentWindow.document;
            iframe_doc.addEventListener('mousedown', function(){
                monitor_p.disconnect(); }); } // アイコンカラー取得終了

        function get_copy(){
            let l_trance=document.querySelector('#l_trance');
            if(l_trance){
                l_trance.value=1; } // 透過度をリセットする
            set_color=color_label.getAttribute('data-color');
            l_color.value='#'+ set_color;
            l_color.style.boxShadow=
                'inset -20px 0 ' + l_color.value;
            monitor_p.disconnect(); // アイコンカラー取得終了

            test_colorE(l_color.value ); // test_colorEを実行 ⏹
            setting[1][2]=l_color.value;
            let write_json=JSON.stringify(setting);
            localStorage.setItem('Draw_Line', write_json); } // ローカルストレージ 保存


        let target_body=document.querySelector('.l-body'); // 監視 target
        let monitor_generator=new MutationObserver(stealth);
        monitor_generator.observe(target_body, {childList: true, subtree: true});

        function stealth(){
            let color_generator=document.querySelector('.ck-l-colorGenerator');
            if(color_generator){
                color_generator.addEventListener('mousedown', function(event){
                    event.stopImmediatePropagation(); }); }}
    } // pick_color()



    function trance(){
        let l_color_code;
        let l_color=document.querySelector('#l_color');
        let l_trance=document.querySelector('#l_trance');
        if(l_trance.value){
            if(test_color(l_color.value)){
                if(l_color.value.length==4){
                    let code_c=l_color.value.split("");
                    l_color_code='#'+code_c[1]+code_c[1]+code_c[2]+code_c[2]+code_c[3]+code_c[3]; }
                else{
                    l_color_code=l_color.value; }

                if(l_trance.value!=1){
                    l_color_code= l_color_code + (100*l_trance.value); }}
            else{
                l_color_code=l_color.value;
                if(l_trance.value==1 && l_color.value.length==9){
                    if(test_color(l_color.value.slice(0, -2))){
                        l_color_code=l_color.value.slice(0, -2); }}
                else if(l_trance.value!=1 && l_color.value.length==9){
                    if(test_color(l_color.value.slice(0, -2))){
                        l_color_code=l_color.value.slice(0, -2) + (100*l_trance.value); }}}
            l_color.value=l_color_code; }}

}