LoginSignup
0
0

More than 3 years have passed since last update.

Qiitaの記事中のソースコードをウインドウの幅で表示するユーザースクリプト

Last updated at Posted at 2020-03-25

 Qiitaは3カラムレイアウトの真ん中のカラムにしかソースコードが表示されず、ウインドウ幅を広くしてもソースコード表示領域はあまり広がりません。

 それでもあまり困らない場合が多かったのですが、最近はSwiftのソースコードをよく見るようになりもっと幅がほしくなったのでユーザースクリプトを作りました。

 これを使ってQiitaのソースコード表示領域にマウスカーソルを乗せると、コピーボタンの横にボタンが増えます。

Buttons.png

 クリックするとウインドウ内のほぼ全面に、モーダルダイアログでソースコードを表示します。×ボタンやモーダルの外をクリックして閉じます。

Dialog.png

ソースコード

// ==UserScript==
// @name         Qiitaの記事中のソースコードをウインドウの幅で表示
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://qiita.com/*/items/*
// @grant        none
// ==/UserScript==

(() => {
  const style = `
    #source-dialog {
      background: #0008;
      border: none;
      position: fixed;
      width: 100%;
      height: 100%;
      z-index: 999;
    }

    #source-dialog > .card {
      background: #364549;
      border-radius: .5rem;
      height: 100%;
      padding: 0 0 1.5rem 0;
      width: 100%;
      display: flex;
      flex-direction: column;
    }

    #source-dialog > .card > .card-header {
      text-align: right;
      padding-bottom: .5rem;
    }

    #source-dialog > .card > .card-header > .close-button {
      color: white;
    }

    #source-dialog > .card > .card-body {
      flex-grow: 1;
      height: 0;
    }

    #source-dialog > .card > .card-body > .code-frame {
      height: 100%;
    }

    #source-dialog > .card > .card-body > .code-frame > .highlight {
      height: 100%;
      overflow-y: auto;
    }

    .code-copy {
      right: 48px !important;
    }

    .code-open-dialog {
      display: none
    }

    .hovering .code-open-dialog {
      display: flex;
      position: absolute;
      transform: translateY(-8px);
      font-size: 1.5rem;
    }

    .it-MdContent .code-open-dialog {
      right: 32px;
    }

    .co-Item .code-open-dialog {
      right: 16px;
    }

    .open-dialog {
      color: #c8c8c8;
      float: right;
      padding: .5rem;
      margin-top: -0.2rem;
    }
    .p-items_article i.open-dialog {
      margin-right: -1.5rem;
    }
  `;

  const element = document.createElement('style');
  element.innerHTML = style;
  document.head.appendChild(element);

  document.body.insertAdjacentHTML('afterbegin', `
    <dialog id="source-dialog">
      <div class="card">
        <div class="card-header">
          <i class="fa fa-fw fa-close fa-lg close-button"></i>
        </div>
      <div class="card-body">
        <div class="code-frame it-MdContent" data-lang=""></div>
      </div>
    </div>
    </dialog>
  `);

  const wrapperElement = document.querySelector('.allWrapper');
  const dialogElement = document.getElementById('source-dialog');
  const cardElement = dialogElement.querySelector(':scope .card');
  const contentElement = cardElement.querySelector(':scope .code-frame');
  const closeButtonElement = cardElement.querySelector(':scope .close-button');

  const showDialog = codeFrameElement => {
    const language = codeFrameElement.getAttribute('data-lang');
    const highlight = codeFrameElement.querySelector(':scope > .highlight');

    const y = document.documentElement.scrollTop;
    wrapperElement.style.top = String(-1 * y) + 'px';
    wrapperElement.style.position = 'fixed';
    wrapperElement.style.width = '100%';
    wrapperElement.setAttribute('data-scroll-y', y);

    contentElement.setAttribute('data-lang', language);
    const clone = highlight.cloneNode(true);
    contentElement.appendChild(clone);

    dialogElement.setAttribute('open', 'open');
  };

  const closeDialog = () => {
    wrapperElement.style.top = null;
    wrapperElement.style.position = null;
    wrapperElement.style.width = null;
    const y = wrapperElement.getAttribute('data-scroll-y');
    document.documentElement.scrollTop = y;

    dialogElement.removeAttribute('open');

    contentElement.innerHTML = '';
  };

  cardElement.addEventListener('click', e => e.stopPropagation());
  closeButtonElement.addEventListener('click', _ => closeDialog());
  dialogElement.addEventListener('click', _ => closeDialog());

  const addIconToCodeFrame = codeFrame => {
    const iconDiv = document.createElement('div');
    iconDiv.className = 'code-open-dialog';

    const hilightDiv = codeFrame.querySelector(':scope .highlight');
    hilightDiv.insertAdjacentElement('beforebegin', iconDiv);

    const iconElement = document.createElement('i');
    iconElement.className = 'open-dialog fa fa-fw fa-external-link';
    iconDiv.insertAdjacentElement('beforeend', iconElement);
    iconElement.addEventListener('click', _ => showDialog(codeFrame));

    codeFrame.addEventListener('mouseover', _ => {
      codeFrame.classList.add('hovering');
    });
    codeFrame.addEventListener('mouseout', _ => {
      codeFrame.classList.remove('hovering');
    });
  };

  for(const codeFrame of document.querySelectorAll('.allWrapper .code-frame')) {
    addIconToCodeFrame(codeFrame);
  }

  const commentsDiv = document.getElementById('comments');
  new MutationObserver(records => {
    records.forEach(record => {
      for(const addedNode of record.addedNodes) {
        if (addedNode.classList.contains('co-Item')) {
          for(const codeFrame of addedNode.querySelectorAll(':scope .code-frame')) {
            console.log('.codeframe', codeFrame);
            addIconToCodeFrame(codeFrame);
          }
        }
      }
    });
  }).observe(commentsDiv, {childList: true});

})();
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0