您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows user to move widgets like the chatbox to the left side of the app.
当前为
// ==UserScript== // @name Add button to move widget bar to left side - tradingview.com // @namespace Itsnotlupus Industries // @match https://www.tradingview.com/* // @grant none // @version 1.0 // @author itsnotlupus // @license MIT // @description Allows user to move widgets like the chatbox to the left side of the app. // ==/UserScript== /* jshint esversion:11 */ // GENERIC UTILITIES /** calls a function whenever the DOM changes */ const observeDOM = (fn, e = document.documentElement, config = { attributes: 1, childList: 1, subtree: 1 }) => { const observer = new MutationObserver(fn); observer.observe(e, config); return () => observer.disconnect(); }; /** check a condition on every DOM change until truthy. async returns truthy value */ const untilDOM = f => new Promise((r,_,d = observeDOM(() => (_=f()) && d() | r(_) )) => 0); const crel = (name, attrs, ...children) => ((e = Object.assign(document.createElement(name), attrs)) => (e.append(...children), e))(); const svg = (name, attrs, ...children) => { const e = document.createElementNS('http://www.w3.org/2000/svg', name); Object.entries(attrs).forEach(([key,val]) => e.setAttribute(key, val)); e.append(...children); return e; } const $ = s => document.querySelector(s); // SCRIPT LOGIC BEGINS HERE. const LEFT_TOOLBAR_WIDTH = 52; const RIGHT_WIDGETBAR_WIDTH = 46; run(); async function run() { // wait for stuff to load await untilDOM(()=>$`.widgetbar-wrap`); // store refs to relevant elements const layoutAreaCenter = $`.layout__area--center`; const layoutAreaTradingPanel = $`.layout__area--tradingpanel`; const layoutAreaLeft = $`.layout__area--left`; const layoutAreaRight = $`.layout__area--right`; const layoutAreaBottom = $`.layout__area--bottom`; const chartContainer = $`.chart-container`; const widgetbarPages = $`.widgetbar-pages`; const widgetbarWrap = $`.widgetbar-wrap`; const widgetbarTabs = $`.widgetbar-tabs`; const drawingToolbar = $`#drawing-toolbar`; const widgetToolbarFiller = $`.widgetbar-tabs div[class^='filler-']`; // This script is inherently tightly coupled to a number of small implementation details on tradingview.com // Whenever the TradingView app is updated, this script may stop working properly. // Here, we attempt to detect fundamental changes and bail out rather than breaking the app. // This is a "best effort" check, and this script might still break future versions of the app. // If that happens, disabling the script and reloading the app should fix things. if (!layoutAreaCenter || !layoutAreaTradingPanel || !layoutAreaLeft || !layoutAreaRight || !layoutAreaBottom || !chartContainer || !widgetbarPages || !widgetbarWrap || !widgetbarTabs || !drawingToolbar || !widgetToolbarFiller) { const msg = 'UserScript "Add button to move widget bar to left side" has become incompatible with TradingView and can no longer work.\nPlease disable or update this script.'; console.log(`%c${msg}`, 'font-weight:600;font-size:2em;color:red'); throw new Error(msg); } // persistent state let widgetsMovedLeft = localStorage.widgetsMovedLeft === 'true'; // augment UI // clone a button and customize it to make our "move widgets" button. const button = widgetToolbarFiller.nextElementSibling.cloneNode(true); button.dataset.name = "move_widgets"; button.title = "Move Widgets to Other Side of Chart"; // l10n schm10n. button.querySelector('svg').remove(); button.querySelector('span').append(svg('svg', { width: 44, height: 44, viewBox: "0 0 21 21" }, // random SVG icon goes here. a UX person I am not. https://www.svgrepo.com/svg/343314/toggles svg('g', { fill:"none", "fill-rule":"evenodd", stroke:"currentColor", "stroke-width":"0.4", "stroke-linecap":"round", "stroke-linejoin":"round", transform:"translate(3 4)" }, svg('circle', { cx:"3.5", cy:"3.5", r:"3"}), svg('path', {d:"M6 1.5h6.5c.8 0 2 .3 2 2s-1.2 2-2 2H6m5.5 8a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"}), svg('path', {d:"M9 8.5H2.5c-.8 0-2 .3-2 2s1.2 2 2 2H9"}) ) )); button.addEventListener('click', () => toggleWidgets()); widgetToolbarFiller.after(button); // apply state to app. toggleWidgets(widgetsMovedLeft); // start observing DOM to adjust layout continuously. observeDOM(adjustLayout); function toggleWidgets(left = !widgetsMovedLeft) { const parent = left ? layoutAreaLeft : layoutAreaRight; parent.prepend(widgetbarWrap); widgetbarTabs.style.right = left ? '' : '0'; widgetbarTabs.style.left = left ? '0' : ''; widgetbarPages.style.right = left ? '51px' : ''; widgetsMovedLeft = left; localStorage.widgetsMovedLeft = left; adjustLayout(); } function adjustLayout() { const rightWidth = widgetsMovedLeft ? 0 : RIGHT_WIDGETBAR_WIDTH; const leftWidth = LEFT_TOOLBAR_WIDTH + (widgetsMovedLeft ? RIGHT_WIDGETBAR_WIDTH + parseInt(widgetbarPages.style.width) : 0); const centerWidth = innerWidth - leftWidth - rightWidth - 8; const centerLeft = leftWidth + 4 set(drawingToolbar, 'width', LEFT_TOOLBAR_WIDTH); set(drawingToolbar, 'marginLeft', leftWidth - LEFT_TOOLBAR_WIDTH + 2); set(layoutAreaRight, 'width', rightWidth); set(layoutAreaLeft, 'width', leftWidth); set(layoutAreaCenter, 'width', centerWidth); set(layoutAreaCenter, 'left', centerLeft); set(chartContainer, 'width', centerWidth); set(layoutAreaBottom, 'width', centerWidth); set(layoutAreaBottom, 'left', centerLeft); set(layoutAreaTradingPanel, 'right', rightWidth); // remove some nags since we're already here. document.querySelector("div[data-role='toast-container']").querySelector('button')?.click(); document.querySelector("[data-dialog-name='gopro']")?.querySelector('button')?.click(); function set(elt, prop, val) { // a gentle style setter that doesn't trigger unnecessary DOM mutations. if (elt.style[prop] !== val +'px') { elt.style[prop] = val + 'px'; } } } }