Up-down arrows buttons for the Tradingview Mobile Site chart

Creates up-down arrows buttons on chart

// ==UserScript==
// @name          Up-down arrows buttons for the Tradingview Mobile Site chart
// @description   Creates up-down arrows buttons on chart
// @author        Konf
// @namespace     https://greasyfork.org/users/424058
// @icon          https://www.google.com/s2/favicons?sz=64&domain=tradingview.com
// @version       1.1.0
// @match         https://www.tradingview.com/*
// @require       https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js#sha512-wkU3qYWjenbM+t2cmvw2ADRRh4opbOYBjkhrPGHV7M6dcE/TR0oKpoDkWXfUs3HrulI2JFuTQyqPLRih1V54EQ==
// @run-at        document-body
// @grant         none
// @noframes
// ==/UserScript==

/* jshint esversion: 8 */

(function() {
  'use strict';

  const DEBUG_OUTLINE = false;       // true adds red outlines to show hitboxes, false hides them

  const BUTTONS_WIDTH = '100%';      // set to 100% to adapt dynamically to price column, or use '50px'
  const BUTTONS_HEIGHT = '60px';     // can't use percents here
  const BUTTONS_BOTTOM_GAP = '40px'; // bottom padding, can't use percents here

  const ARROWS_SCALE = 1;      // can be fractional values like 1.4, maybe width is not even needed
  const ARROWS_WIDTH = '40px'; // can not go well over 100%, use scale instead
  const ARROWS_OPACITY = 0.8;  // 1 is fully visible and 0 is fully transparent, 0.5 is half
  const ARROW_UP_SRC = 'https://img.icons8.com/fluency-systems-filled/2962ff/128/triangle.png';
  const ARROW_DOWN_SRC = 'https://img.icons8.com/fluency-systems-filled/2962ff/128/triangle.png';
  const ARROW_UP_ROTATE = 0;   // clockwise, may take negative values
  const ARROW_DOWN_ROTATE = 180;

  const Q = {
    priceAxis: 'div.price-axis',
  };

  document.arrive(Q.priceAxis, { existing: true }, (priceAxis) => {
    const btnsContainer = document.createElement('div');
    const btnUp = document.createElement('button');
    const btnDown = document.createElement('button');

    btnUp.addEventListener('click', () => pressKey('ArrowUp', 38));
    btnDown.addEventListener('click', () => pressKey('ArrowDown', 40));

    for (const btn of [btnUp, btnDown]) {
      btn.style.width = '100%';
      btn.style.height = BUTTONS_HEIGHT;
      btn.style.padding = '0';
      btn.style.boxSizing = 'border-box';
      btn.style.cursor = 'pointer';
      btn.style.overflow = 'hidden';
      btn.style.background = 'none';

      if (DEBUG_OUTLINE) {
        btn.style.border = '1px solid red';
      } else {
        btn.style.border = 'none';
      }

      const img = document.createElement('img');

      img.style.scale = ARROWS_SCALE;
      img.style.width = ARROWS_WIDTH;
      img.style.opacity = ARROWS_OPACITY;

      btn.append(img);
    }

    btnUp.firstChild.src = ARROW_UP_SRC;
    btnDown.firstChild.src = ARROW_DOWN_SRC;

    if (ARROW_UP_ROTATE) {
      btnUp.firstChild.style.transform = `rotate(${ARROW_UP_ROTATE}deg)`;
    }

    if (ARROW_DOWN_ROTATE) {
      btnDown.firstChild.style.transform = `rotate(${ARROW_DOWN_ROTATE}deg)`;
    }

    btnsContainer.style.width = BUTTONS_WIDTH;
    btnsContainer.style.position = 'absolute';
    btnsContainer.style.bottom = BUTTONS_BOTTOM_GAP;
    btnsContainer.style.right = '0';
    btnsContainer.style.zIndex = '3';

    for (const el of [
      btnsContainer, btnUp, btnDown, btnUp.firstChild, btnDown.firstChild,
    ]) {
      el.style.userSelect = 'none';
      el.setAttribute('draggable', 'false');
      el.addEventListener('dragstart', (ev) => {
        ev.preventDefault();
        ev.stopImmediatePropagation();
      });
    }

    btnsContainer.append(btnUp, btnDown);
    priceAxis.parentElement.append(btnsContainer);
  });

  // utils ----------------------------------------------------------------------

  function pressKey(key, code) {
    const event = new KeyboardEvent('keydown', {
      key,
      code,
      keyCode: code,
      which: code,
      bubbles: true,
    });

    document.body.dispatchEvent(event);
  }

  // ---------------------------------------------------------------------- utils
}());