Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name EyeWire II MEC - Image Adjustment Controls
- // @namespace https://connectome.quest/
- // @version 1.3.0
- // @description MEC-only draggable contrast, luminosity, hue and sharpness controls
- // @match *://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
- // @match *://*.brain-wire-test.org/*
- // @match *://*.eyewire.org/*
- // @match *://*.r.appspot.com/*
- // @run-at document-idle
- // @grant none
- // ==/UserScript==
- (function () {
- 'use strict';
- if (window.__EW_MEC_IMAGE_CONTROLS__) return;
- window.__EW_MEC_IMAGE_CONTROLS__ = true;
- const STORAGE_KEY = 'ew_mec_image_controls_v1';
- const defaults = {
- contrast: 100,
- brightness: 100,
- hue: 0,
- sharpness: 0,
- side: 'left',
- x: 20,
- y: 100,
- collapsed: true
- };
- let saved = {};
- try {
- saved = JSON.parse(
- localStorage.getItem(STORAGE_KEY)
- ) || {};
- } catch (e) {
- saved = {};
- }
- const settings = Object.assign({}, defaults, saved);
- const originalStyles = new Map();
- function save() {
- try {
- localStorage.setItem(
- STORAGE_KEY,
- JSON.stringify(settings)
- );
- } catch (e) {
- console.warn('[MEC Controls] Save failed:', e);
- }
- }
- // --------------------------------------------------
- // MEC DATASET DETECTION
- // --------------------------------------------------
- const MEC_IDENTIFIERS = [
- 'mec_alignment_2025-09',
- 'pni_mec'
- ];
- function isMECDataset() {
- let state = window.location.hash || '';
- try {
- state = decodeURIComponent(state);
- } catch (e) {
- // Use original state.
- }
- state = state.toLowerCase();
- return MEC_IDENTIFIERS.some(
- identifier => state.includes(identifier)
- );
- }
- // --------------------------------------------------
- // SHARPENING FILTER
- // --------------------------------------------------
- const svgNS = 'http://www.w3.org/2000/svg';
- const svg = document.createElementNS(svgNS, 'svg');
- svg.setAttribute('width', '0');
- svg.setAttribute('height', '0');
- svg.style.position = 'absolute';
- svg.style.pointerEvents = 'none';
- const defs = document.createElementNS(svgNS, 'defs');
- const filter = document.createElementNS(svgNS, 'filter');
- filter.id = 'ew-mec-sharpen';
- filter.setAttribute('x', '0');
- filter.setAttribute('y', '0');
- filter.setAttribute('width', '100%');
- filter.setAttribute('height', '100%');
- filter.setAttribute(
- 'color-interpolation-filters',
- 'sRGB'
- );
- const convolution = document.createElementNS(
- svgNS,
- 'feConvolveMatrix'
- );
- convolution.setAttribute('order', '3');
- convolution.setAttribute('edgeMode', 'duplicate');
- convolution.setAttribute('preserveAlpha', 'true');
- filter.appendChild(convolution);
- defs.appendChild(filter);
- svg.appendChild(defs);
- document.documentElement.appendChild(svg);
- function updateSharpenKernel() {
- const amount = settings.sharpness / 25;
- const kernel = [
- 0, -amount, 0,
- -amount, 1 + 4 * amount, -amount,
- 0, -amount, 0
- ];
- convolution.setAttribute(
- 'kernelMatrix',
- kernel.join(' ')
- );
- convolution.setAttribute('divisor', '1');
- }
- // --------------------------------------------------
- // FIND NEUROGLANCER CANVASES
- // --------------------------------------------------
- function getCandidateCanvases() {
- return Array.from(
- document.querySelectorAll('canvas')
- ).filter(canvas => {
- if (canvas.closest('#ew-mec-controls')) {
- return false;
- }
- const rect = canvas.getBoundingClientRect();
- const style = getComputedStyle(canvas);
- return (
- rect.width > 150 &&
- rect.height > 150 &&
- style.display !== 'none' &&
- style.visibility !== 'hidden'
- );
- });
- }
- function getTargetCanvases() {
- const canvases = getCandidateCanvases();
- if (!canvases.length) return [];
- const midpoint = window.innerWidth / 2;
- return canvases.filter(canvas => {
- const rect = canvas.getBoundingClientRect();
- const centerX = rect.left + rect.width / 2;
- return settings.side === 'left'
- ? centerX < midpoint
- : centerX >= midpoint;
- });
- }
- // --------------------------------------------------
- // APPLY AND RESTORE FILTERS
- // --------------------------------------------------
- function restoreCanvas(canvas) {
- const original = originalStyles.get(canvas);
- if (!original) return;
- canvas.style.filter = original.filter;
- canvas.style.webkitFilter =
- original.webkitFilter;
- originalStyles.delete(canvas);
- }
- function restoreAllCanvases() {
- for (const canvas of Array.from(
- originalStyles.keys()
- )) {
- restoreCanvas(canvas);
- }
- }
- function applyFilters() {
- // Never apply adjustments outside MEC.
- if (!isMECDataset()) {
- restoreAllCanvases();
- return;
- }
- updateSharpenKernel();
- const targets = new Set(
- getTargetCanvases()
- );
- for (const canvas of Array.from(
- originalStyles.keys()
- )) {
- if (
- !canvas.isConnected ||
- !targets.has(canvas)
- ) {
- restoreCanvas(canvas);
- }
- }
- const filterParts = [
- `contrast(${settings.contrast}%)`,
- `brightness(${settings.brightness}%)`,
- `hue-rotate(${settings.hue}deg)`
- ];
- if (settings.sharpness > 0) {
- filterParts.push(
- 'url(#ew-mec-sharpen)'
- );
- }
- const filterString = filterParts.join(' ');
- for (const canvas of targets) {
- if (!originalStyles.has(canvas)) {
- originalStyles.set(canvas, {
- filter: canvas.style.filter,
- webkitFilter:
- canvas.style.webkitFilter
- });
- }
- canvas.style.filter = filterString;
- canvas.style.webkitFilter = filterString;
- }
- }
- // --------------------------------------------------
- // FLOATING USER INTERFACE
- // --------------------------------------------------
- const root = document.createElement('div');
- root.id = 'ew-mec-controls';
- // Hidden until MEC is detected.
- root.style.display = 'none';
- const style = document.createElement('style');
- style.textContent = `
- #ew-mec-controls {
- position: fixed;
- z-index: 2147483647;
- width: 260px;
- color: #eef3ff;
- font: 13px Arial, sans-serif;
- user-select: none;
- box-sizing: border-box;
- }
- #ew-mec-controls * {
- box-sizing: border-box;
- }
- #ew-mec-controls .ew-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 8px;
- background: #19283d;
- border: 1px solid #526d91;
- border-radius: 9px;
- padding: 9px 11px;
- cursor: move;
- box-shadow: 0 4px 18px #0008;
- touch-action: none;
- }
- #ew-mec-controls .ew-title {
- font-weight: bold;
- font-size: 13px;
- white-space: nowrap;
- }
- #ew-mec-controls button {
- color: #eef3ff;
- background: #344b68;
- border: 1px solid #607b9c;
- border-radius: 5px;
- padding: 5px 9px;
- cursor: pointer;
- }
- #ew-mec-controls button:hover {
- background: #49698e;
- }
- #ew-mec-controls .ew-body {
- margin-top: 4px;
- padding: 13px;
- background: rgba(19, 29, 44, 0.97);
- border: 1px solid #526d91;
- border-radius: 9px;
- box-shadow: 0 5px 20px #0009;
- }
- #ew-mec-controls .ew-control {
- margin-bottom: 15px;
- }
- #ew-mec-controls .ew-label {
- display: flex;
- justify-content: space-between;
- margin-bottom: 7px;
- }
- #ew-mec-controls .ew-value {
- color: #8ecbff;
- font-variant-numeric: tabular-nums;
- }
- #ew-mec-controls input[type="range"] {
- width: 100%;
- margin: 0;
- cursor: pointer;
- accent-color: #65b7ff;
- }
- #ew-mec-controls select {
- width: 100%;
- padding: 7px;
- color: white;
- background: #293c55;
- border: 1px solid #526d91;
- border-radius: 5px;
- }
- #ew-mec-controls .ew-reset {
- width: 100%;
- margin-top: 12px;
- }
- #ew-mec-controls .ew-note {
- font-size: 10px;
- line-height: 1.4;
- color: #9daec3;
- margin-top: 11px;
- }
- `;
- document.head.appendChild(style);
- root.innerHTML = `
- <div class="ew-header">
- <span class="ew-title">◐ MEC Image Controls</span>
- <button class="ew-toggle"
- title="Expand or collapse">
- ${settings.collapsed ? '+' : '−'}
- </button>
- </div>
- <div class="ew-body"
- style="display:${
- settings.collapsed ? 'none' : 'block'
- }">
- <div class="ew-sliders"></div>
- <label class="ew-label"
- for="ew-target-pane">
- Target pane
- </label>
- <select id="ew-target-pane">
- <option value="left">
- Left pane (2D)
- </option>
- <option value="right">
- Right pane
- </option>
- </select>
- <button class="ew-reset">
- Reset adjustments
- </button>
- <div class="ew-note">
- MEC dataset only. Visual adjustments
- do not change the underlying data
- or annotations.
- </div>
- </div>
- `;
- document.body.appendChild(root);
- const header = root.querySelector('.ew-header');
- const toggle = root.querySelector('.ew-toggle');
- const body = root.querySelector('.ew-body');
- const sliders = root.querySelector('.ew-sliders');
- const targetSelect =
- root.querySelector('#ew-target-pane');
- const resetButton =
- root.querySelector('.ew-reset');
- // --------------------------------------------------
- // SLIDERS
- // --------------------------------------------------
- const controls = [
- {
- key: 'contrast',
- label: 'Contrast',
- min: 0,
- max: 300,
- step: 1,
- unit: '%'
- },
- {
- key: 'brightness',
- label: 'Luminosity',
- min: 0,
- max: 300,
- step: 1,
- unit: '%'
- },
- {
- key: 'hue',
- label: 'Hue',
- min: -180,
- max: 180,
- step: 1,
- unit: '°'
- },
- {
- key: 'sharpness',
- label: 'Sharpness',
- min: 0,
- max: 100,
- step: 1,
- unit: '%'
- }
- ];
- const sliderElements = {};
- for (const control of controls) {
- const wrapper = document.createElement('div');
- wrapper.className = 'ew-control';
- const label = document.createElement('div');
- label.className = 'ew-label';
- const name = document.createElement('span');
- name.textContent = control.label;
- const value = document.createElement('span');
- value.className = 'ew-value';
- const slider = document.createElement('input');
- slider.type = 'range';
- slider.min = control.min;
- slider.max = control.max;
- slider.step = control.step;
- label.append(name, value);
- wrapper.append(label, slider);
- sliders.appendChild(wrapper);
- sliderElements[control.key] = {
- slider,
- value,
- control
- };
- slider.addEventListener('input', () => {
- settings[control.key] =
- Number(slider.value);
- updateSliderDisplays();
- applyFilters();
- save();
- });
- }
- function updateSliderDisplays() {
- for (const control of controls) {
- const entry =
- sliderElements[control.key];
- entry.slider.value =
- settings[control.key];
- entry.value.textContent =
- settings[control.key] +
- control.unit;
- }
- }
- targetSelect.value = settings.side;
- targetSelect.addEventListener('change', () => {
- settings.side = targetSelect.value;
- applyFilters();
- save();
- });
- resetButton.addEventListener('click', () => {
- settings.contrast = 100;
- settings.brightness = 100;
- settings.hue = 0;
- settings.sharpness = 0;
- updateSliderDisplays();
- applyFilters();
- save();
- });
- toggle.addEventListener('click', event => {
- event.stopPropagation();
- settings.collapsed = !settings.collapsed;
- body.style.display =
- settings.collapsed ? 'none' : 'block';
- toggle.textContent =
- settings.collapsed ? '+' : '−';
- save();
- });
- updateSliderDisplays();
- // --------------------------------------------------
- // DRAGGABLE PANEL
- // --------------------------------------------------
- function clampPosition() {
- const maxX = Math.max(
- 0,
- window.innerWidth - root.offsetWidth
- );
- const maxY = Math.max(
- 0,
- window.innerHeight - header.offsetHeight
- );
- settings.x = Math.max(
- 0,
- Math.min(settings.x, maxX)
- );
- settings.y = Math.max(
- 0,
- Math.min(settings.y, maxY)
- );
- root.style.left = settings.x + 'px';
- root.style.top = settings.y + 'px';
- }
- // Set coordinates even when the panel is hidden.
- root.style.left = settings.x + 'px';
- root.style.top = settings.y + 'px';
- let dragging = false;
- let dragOffsetX = 0;
- let dragOffsetY = 0;
- header.addEventListener('pointerdown', event => {
- if (event.target.closest('button')) {
- return;
- }
- dragging = true;
- const rect = root.getBoundingClientRect();
- dragOffsetX = event.clientX - rect.left;
- dragOffsetY = event.clientY - rect.top;
- header.setPointerCapture(event.pointerId);
- event.preventDefault();
- });
- header.addEventListener('pointermove', event => {
- if (!dragging) return;
- settings.x = event.clientX - dragOffsetX;
- settings.y = event.clientY - dragOffsetY;
- clampPosition();
- });
- function stopDragging() {
- if (!dragging) return;
- dragging = false;
- save();
- }
- header.addEventListener('pointerup', stopDragging);
- header.addEventListener('pointercancel', stopDragging);
- window.addEventListener('resize', () => {
- if (isMECDataset()) {
- clampPosition();
- }
- applyFilters();
- });
- // --------------------------------------------------
- // AUTOMATIC DATASET SWITCHING
- // --------------------------------------------------
- let lastDatasetState = null;
- function updateDatasetVisibility() {
- const isMEC = isMECDataset();
- if (isMEC !== lastDatasetState) {
- lastDatasetState = isMEC;
- if (isMEC) {
- root.style.display = 'block';
- clampPosition();
- applyFilters();
- console.log(
- '[MEC Controls] MEC detected. Controls enabled.'
- );
- } else {
- root.style.display = 'none';
- restoreAllCanvases();
- console.log(
- '[MEC Controls] Non-MEC dataset. Controls disabled.'
- );
- }
- }
- }
- window.addEventListener(
- 'hashchange',
- updateDatasetVisibility
- );
- // Detect dataset changes made without reloading.
- setInterval(updateDatasetVisibility, 1500);
- // Keep filters applied to newly created canvases,
- // but only while MEC is active.
- let scheduled = false;
- function scheduleApply() {
- if (scheduled || !isMECDataset()) {
- return;
- }
- scheduled = true;
- requestAnimationFrame(() => {
- scheduled = false;
- if (isMECDataset()) {
- applyFilters();
- }
- });
- }
- const observer = new MutationObserver(
- scheduleApply
- );
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- setInterval(() => {
- if (isMECDataset()) {
- applyFilters();
- }
- }, 2000);
- // Initial detection.
- updateDatasetVisibility();
- console.log(
- '[EyeWire MEC Controls] v1.3.0 initialized.'
- );
- })();
Advertisement