「文字色」の修飾を実用的にする 

「LinkCard Editor」(4) で纏めましたが、「リンクカード」内の文字編集で、特定の編集アイコン操作をするとカードデザインが崩れます。

 

ただし「文字色」の修飾は、「記事タイトル」「記事の出だし」等の要素全体に対して指定すれば、崩れない事が判りました。 カードの文字編集で「文字色」の指定は欲しい修飾内容なので、これを実用的にする事を考えました。

 

要は「要素」の文字列を全選択できれば、「文字色」の修飾ができます。「記事タイトル」を全選択するのは手作業でも簡単ですが、「記事の出だし」は長い文字列が表示されず隠されている場合が多く、その「全選択」の操作が難しいのが問題です。

 

この「全選択」(反転指定)をスクリプトで扱う場合は、「selection」や「range」といった、他のコードとは一線を画したコードが必要になります。 これは奥が深くなかなか扱いきれませんが、そこは調べながらです。 今回の更新によって、「リンクカード」上の文字編集が少し扱い易くなりました。

 

 

 

文字列の全選択操作 

「リンクカード」上の文字部分は、「記事タイトル」「記事の出だし」「URL表示」の3箇所に限られます。 それらの文字列を「Ctrl+左クリック」すると、要素単位で全選択できます。 なお、部分的な選択は、通常通りのキャレット操作で可能です。

 

〔追記〕2020.01.08
このショートカットは ver. 1.3以降「Shift+左クリック」に変更しました。

 

 

❶ 下は、編集選択をしたリンクカードで「青枠」が表示されています。

 

 

❷「記事タイトル」上の「Ctrl+左クリック」で「タイトル文字」が全選択されます。

 

 

❸「記事の出だし」上の「Ctrl+左クリック」で「記事の出だし」が全選択されます。

 

 

❹「URL表示」上の「Ctrl+左クリック」で「URL表示の文字列」が全選択されます。

 

 

 

 

「記事の出だし」の文字色指定 

「記事タイトル」「URL表示」は、要素の文字列が全て表示されているて「文字色」の修飾などで 全選択 → 修飾指定 がほぼ問題なく可能です。 しかし「小型化」と「記事の出だし」の「文字色」指定を行う場合は、「小型化」の処理順序を先にする必要があります。

 

〔追記〕2020.01.12
この問題は ver. 1.7 以降は改善され、処理順を気にする必要はありません。

 

 

「文字色」→「小型化」の操作順 

❸「記事の出だし」上の「Ctrl+左クリック」で「記事の出だし」が全選択されます。

 

 

❺ この状態から編集アイコンで「文字色」(赤)を指定。

 

 

❻ 更に「小型化」を指定。

この ❸→❺→❻ の順序の操作では「記事の出だし」が 1行表示のままです。

 

 

 

「小型化」→「文字色」の操作順 

❼ 先に「小型化」を指定します。

 

 

❽「記事の出だし」上の「Ctrl+左クリック」で「記事の出だし」を全選択します。 

 

 

❾ この状態から編集アイコンで「文字色」(赤)を指定。

この ❼→❽→❾ の操作では、「記事の出だし」が正常な複数行表示になります。

 

 

以上の様に、「文字色」の指定より先に「小型化」を指定するべきです。「小型化」と「記事の出だし」の「文字色」指定が重なる場合は、この処理順が必須です。

 

 

 

「リンクカード」の背景色アレンジ 機能

暗背景スキンの場合は「リンクカード」の白背景が目立ち過ぎ、カード背景色の明度を下げたい場合があるかも知れません。 また、カード背景色を濃くして、文字色を白反転させるデザインもありです。 カードの背景色を変更できれば、ページデザインも色々と可能性が拡がります。

 

今回は「カラー設定 input要素」を使い、背景色を設定可能にしました。 しかし、ブラウザデフォルトのカラーパレットは「淡色」の指定が難しく、できれば編集画面のカラーパレットを使いたいところです。 しかし、これは後の課題です。

 

● 上部の「コントロールバー」上に「背景色」の「input要素」を配置しました。 このボタンを「左クリック」すると、下の様に「カラーパレット」が表示されます。

 

● パレットのピッカー部をクリックすると、その場所の色が「選択色」になります。

 

 

●「背景色」のボタンを「Ctrl + 左クリック」すると、編集可能で「青枠」が表示されている「リンクカード」に、「選択色」が背景色として設定されます。

 

 

 

 

 「LinkCard Editor」ver. 0.4

このツールは Chrome・Edge / Firefox 版の「Tampermonkey」で動作を確認しています。「Tampermonkey」に以下のコードを登録する事で、このツールを利用することが出来ます。

 

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

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

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

 

 

〔 LinkCard Editor 〕ver. 0.4

 

// ==UserScript==
// @name         LinkCard Editor ⭐
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  「通常表示」上のリンクカードを編集 「Ctrl+F6」
// @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 mode=0; // Card編集状態

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


function main(){
    let editor_iframe;
    let iframe_doc;
    let iframe_body;
    let wysiwyg; // 通常表示の iframe内 html
    let selection;
    let range;


    let target0=document.querySelector('#cke_1_contents'); // 監視 target
    let monitor0=new MutationObserver( catch_key );
    monitor0.observe(target0, {childList: true}); // ショートカット待受け開始

    catch_key();

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

            when_back();

            iframe_doc=editor_iframe.contentWindow.document;
            iframe_doc.addEventListener('keydown', check_key); // iframe内
            document.addEventListener('keydown', check_key); // iframe外

            function check_key(event){
                if(event.ctrlKey && event.keyCode==117){ // ショートカット「Crtl+F6」
                    event.stopImmediatePropagation();
                    if(mode==0 && editor_iframe){
                        mode=1;
                        sign();
                        card_edit(); }
                    else if(mode==1 && editor_iframe){
                        mode=0;
                        sign_clear();
                        card_close(); }}}}

        before_end();
    } // catch_key()


    function when_back(){
        if(mode==1){
            sign();
            card_edit_r();
            card_edit(); }}


    function card_close(){

        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){ // WYSIWYG表示が実行条件
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                iframe_body=iframe_doc.querySelector('body');
                if(iframe_body){
                    selection=iframe_doc.getSelection(); //Selection取得
                    range=iframe_doc.createRange(); //Range生成
                    selection.removeAllRanges();

                    let target_card=iframe_body.querySelectorAll('.ogpCard_root');
                    for(let k=0; k<target_card.length; k++){
                        if(target_card[k].classList.contains('edit_card')){
                            target_card[k].classList.remove('edit_card');
                            let ogpCard_wrap=target_card[k].querySelector('.ogpCard_wrap');
                            if(ogpCard_wrap){
                                ogpCard_wrap.setAttribute('contenteditable', 'false'); }}}}}}}


    function card_edit(){
        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){ // WYSIWYG表示が実行条件
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                iframe_body=iframe_doc.querySelector('body');
                if(iframe_body){
                    let target_card=iframe_body.querySelectorAll('.ogpCard_root');
                    for(let k=0; k<target_card.length; k++){
                        target_card[k].addEventListener('click', function(event){
                            event.preventDefault();
                            if(event.ctrlKey){
                                set_card(target_card[k]); }}); }

                    function set_card(card){
                        if(mode==1){
                            card_close();
                            card.classList.add('edit_card');
                            edit_in(card); }}
                }}}} // card_edit()


    function card_edit_r(){
        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){ // WYSIWYG表示が実行条件
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                iframe_body=iframe_doc.querySelector('body');
                if(iframe_body){
                    let target_card=iframe_body.querySelectorAll('.ogpCard_root');
                    for(let k=0; k<target_card.length; k++){
                        if(target_card[k].classList.contains('edit_card')){
                            edit_in(target_card[k]); }}}}}}


    function edit_in(card){
        if(mode==1){
            let ogpCard_wrap=card.querySelector('.ogpCard_wrap');
            if(ogpCard_wrap){
                ogpCard_wrap.setAttribute('contenteditable', 'true'); } // 編集可能にする

            let card_link=card.querySelector('.ogpCard_link');
            if(!card.querySelector('.ogpCard_imageWrap')){
                let imgw=document.createElement('span');
                imgw.setAttribute('class', 'ogpCard_imageWrap');
                imgw.setAttribute('style', 'position:relative;width:120px;height:120px;flex-shrink:0');
                card_link.appendChild(imgw); } // カバー画像が無い場合に表示エリアを確保する

            arrange_in(card);
            bgcolor_set(card);
            select_span();
            item_setter(); }} // edit_in()


    function item_setter(){
        let target1=document.querySelector('#js-photos-imageList'); // 監視 target
        let monitor1=new MutationObserver(item_select);
        monitor1.observe(target1, {childList: true, subtree: true}); // 画像パレット監視

        item_select();

        function item_select(){
            let item=document.querySelectorAll('.p-images-imageList__item');
            for(let k=0; k<item.length; k++){
                item[k].addEventListener('click', function(event){
                    set_img(event, item[k]); }); }}

        function set_img(e, item){
            if(mode==1){
                e.stopImmediatePropagation();
                let img_src=item.getAttribute('data-image');
                let inner=
                    '<img alt="" class="ogpCard_image" data-ogp-card-image="" height="120" '+
                    'loading="lazy" data-cke-saved-src="' + img_src +
                    '" src="' + img_src +
                    '" style="position:absolute;top:50%;left:50%;object-fit:cover;'+
                    'min-height:100%;min-width:100%;'+
                    'transform:translate(-50%,-50%)" width="120">';

                editor_iframe=document.querySelector('.cke_wysiwyg_frame');
                if(editor_iframe){ // WYSIWYG表示が実行条件
                    iframe_doc=editor_iframe.contentWindow.document;
                    if(iframe_doc){
                        iframe_body=iframe_doc.querySelector('body');
                        if(iframe_body){
                            let target_card=iframe_body.querySelectorAll('.ogpCard_root');
                            for(let k=0; k<target_card.length; k++){
                                if(target_card[k].classList.contains('edit_card')){
                                    range_con(target_card[k]);
                                    let card_imgw=target_card[k].querySelector('.ogpCard_imageWrap');
                                    if(card_imgw){
                                        card_imgw.innerHTML=inner; }}}

                            function range_con(card){
                                if(mode==1){
                                    selection=iframe_doc.getSelection(); //Selection取得
                                    range=iframe_doc.createRange(); //Range生成
                                    range.setStart(card, 0);
                                    range.setEnd(card, 0);
                                    selection.removeAllRanges();
                                    selection.addRange(range); }} // 選択Cardにselectionを指定
                        }}}}} // set_img()
    } // item_setter()


    function arrange_in(card){
        let al=document.querySelector('#disp_le #al');
        let ac=document.querySelector('#disp_le #ac');
        let ar=document.querySelector('#disp_le #ar');
        al.onclick=function(){ arrange_setting(0); }
        ac.onclick=function(){ arrange_setting(1); }
        ar.onclick=function(){ arrange_setting(2); }

        function arrange_setting(n){
            if(card.classList.contains('edit_card')){
                if(n==0){
                    card.style.textAlign='left'; }
                else if(n==1){
                    card.style.textAlign='center'; }
                else if(n==2){
                    card.style.textAlign='right'; }

                let link=card.querySelector('.ogpCard_link');
                if(link){
                    link.style.width='500px';
                    link.style.height='108px';
                    link.style.margin='0 auto';
                    link.style.border='1px solid #009688'; }
                let content=card.querySelector('.ogpCard_content');
                if(content){
                    content.style.padding='8px 25px 4px 15px'; }
                let title=card.querySelector('.ogpCard_title');
                if(title){
                    title.style.flexShrink='0';
                    title.style.font='bold 16px/1.25 Meiryo';
                    slim_title(card); }
                let description=card.querySelector('.ogpCard_description');
                if(description){
                    description.style.whiteSpace='unset';
                    description.style.margin='0';
                    description.style.font='13px/1.4 Meiryo'; }
                let imageW=card.querySelector('.ogpCard_imageWrap');
                if(imageW){
                    imageW.style.width='98px';
                    imageW.style.height='98px';
                    imageW.style.top='3px';
                    imageW.style.right='15px';
                    imageW.style.border='1px solid #eee'; }

                function slim_title(card){ // 先頭・末尾が『』の場合にカッコを削除
                    let link=card.querySelector('.ogpCard_link');
                    if(link.getAttribute('href').includes('https://ameblo.jp/')){
                        let title=card.querySelector('.ogpCard_title');
                        if(title){
                            let title_str=title.textContent;
                            if(title_str.slice(0, 1)=='『' && title_str.slice(-1)=='』'){
                                title_str=title_str.slice(1).slice(0, -1); }
                            title.textContent=title_str; }}}
            }}} // arrange_in()


    function bgcolor_set(card){
        let link=card.querySelector('.ogpCard_link');
        let color=document.querySelector('#lc_color');
        if(color){
            color.onclick=function(event){
                if(event.ctrlKey){
                    event.preventDefault();
                    if(card.classList.contains('edit_card')){
                        link.style.backgroundColor=color.value; }}}}}


    function select_span(){
        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){ // WYSIWYG表示が実行条件
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                iframe_doc.onclick=function(event){
                    if(event.ctrlKey){
                        let elem=iframe_doc.elementFromPoint(event.clientX, event.clientY);

                        let closest_ogp=elem.closest('.ogpCard_root');
                        if(closest_ogp && closest_ogp.classList.contains('edit_card')){
                            if(elem.classList.contains('ogpCard_title') ||
                               elem.classList.contains('ogpCard_description') ||
                               elem.classList.contains('ogpCard_urlText') ||
                               elem.parentNode.classList.contains('ogpCard_description')){
                                selection=iframe_doc.getSelection(); //Selection取得
                                range=iframe_doc.createRange(); //Range生成
                                range.selectNodeContents(elem);
                                selection.removeAllRanges();
                                selection.addRange(range); }}}}}}}



    function sign(){
        monitor0.disconnect(); // 起動時セットアップを MutationObserverに反応させない

        let disp=document.createElement("div");
        disp.setAttribute("id", "disp_le");
        disp.innerHTML=
            '   ▢ LinkCard Editor カード指定:Ctrl+Click 小型化と配置:'+
            '<span id="al">◀</span><span id="ac"> ▇ </span><span id="ar">▶</span>'+
            ' 背景色:<input type="color" id="lc_color" value="#ffffff">';

        let css=
            '#cke_1_contents { display: flex; flex-direction: column; } '+
            '#disp_le { display: none; margin: 0 0 9px; padding: 4px 0 2px; '+
            'font: normal 16px Meiryo; color: #fff; background: #607d8b; } '+
            '#ac { font-size: 14px; vertical-align: 1px; } '+
            '#al, #ac, #ar { cursor: pointer; } '+
            '#lc_color { width: 20px; height: 20px; }';

        let style=document.createElement('style'); // disp_le のデザインを指定
        style.setAttribute("id", "le_style");
        style.innerHTML=css;
        disp.appendChild(style);

        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){
            if(!target0.querySelector('#disp_le')){
                target0.insertBefore(disp, editor_iframe); }}

        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        if(editor_iframe){
            iframe_doc=editor_iframe.contentWindow.document;
            if(iframe_doc){
                let iframe_html=iframe_doc.querySelector('html');
                iframe_body=iframe_doc.querySelector('body');
                if(iframe_html && iframe_body){
                    let card_style= iframe_doc.createElement('style'); // 選択Card のデザインを指定
                    let css='.edit_card { outline: 2px solid #2196f3; }';
                    card_style.setAttribute("id", "card_style");
                    card_style.innerHTML=css;
                    if(!iframe_html.querySelector('#card_style')){
                        iframe_html.appendChild(card_style); }}}}


        let disp_le=document.querySelector('#disp_le');
        disp_le.style.display='block';

        let photos_w=document.querySelector('#js-photos-wrapper');
        photos_w.style.outline='2px solid #2196f3'; // 画像パレットに青枠表示

        monitor0.observe(target0, {childList: true});
    } // sign()


    function sign_clear(){
        if(target0.querySelector('#disp_le')){
            target0.querySelector('#disp_le').style.display='none'; } // 起動表示を非表示
        let photos_w=document.querySelector('#js-photos-wrapper');
        photos_w.style.outline=''; } // 画像パレットの青枠を削除


    function before_end(){
        editor_iframe=document.querySelector('.cke_wysiwyg_frame');
        let submitButton=document.querySelectorAll('.js-submitButton');
        submitButton[0].addEventListener("click", all_close, false);
        submitButton[1].addEventListener("click", all_close, false);

        function all_close(){
            if(mode==1){
                if(!editor_iframe){ //「HTML表示」編集画面の場合
                    alert("⛔ LinkCard Editor が処理を終了していません\n\n"+
                          "   通常表示画面に戻り 編集を終了してください");
                    event.stopImmediatePropagation();
                    event.preventDefault(); }
                else if(editor_iframe){ //「通常表示」編集画面の場合
                    mode=0;
                    card_close(); }}}
    } // before_end()

} // main()


 

 

 

「LinkCard Editor ⭐」最新版について 

旧いバージョンの「LinkCard Editor ⭐」は、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。 最新バージョンへのリンクは、以下のページのリンクリストから探せます。