Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name EyeWire II - UI Guard
- // @namespace local.eyewire.ui
- // @version 2.1.0
- // @description Stable legend, annotations, radio and Selection Details; experimental Cut/Merge layer-switch guard.
- // @match https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
- // @run-at document-start
- // @grant none
- // ==/UserScript==
- (() => {
- 'use strict';
- const LOG = '[EW Guard]';
- const LEGEND = '.nge-seg-legend';
- const ANNOTATIONS_PANEL =
- '.neuroglancer-side-panel.nge-ann-panel';
- const ANNOTATIONS_TAB =
- '.neuroglancer-layer-side-panel-tab-view';
- const SELECTION_PANEL =
- '.neuroglancer-side-panel.neuroglancer-selection-details';
- const CLOSE_BUTTON =
- '.neuroglancer-icon[title="Close panel"]';
- const SETTINGS_BUTTON =
- 'button.nge-settings-advanced-btn';
- const STORAGE_KEY =
- 'ew-ui-guard-selection-closed';
- const EDITING = [
- '.nge-split-merge-overlay',
- '.graphene-merge-segments',
- '.graphene-multicut'
- ].join(', ');
- function editingActive() {
- return !!document.querySelector(EDITING);
- }
- function important(el, property, value) {
- if (
- el.style.getPropertyValue(property) !== value ||
- el.style.getPropertyPriority(property) !== 'important'
- ) {
- el.style.setProperty(property, value, 'important');
- }
- }
- // ============================================
- // 1. STATIONARY LEGEND
- // ============================================
- let legend = null;
- let legendPosition = null;
- function updateLegend() {
- const found = document.querySelector(LEGEND);
- if (!found) {
- legend = null;
- legendPosition = null;
- return;
- }
- if (found !== legend) {
- legend = found;
- legendPosition = null;
- }
- if (!legendPosition && !editingActive()) {
- const r = found.getBoundingClientRect();
- if (r.width >= 10 && r.height >= 5) {
- legendPosition = {
- left: r.left,
- top: r.top,
- width: r.width
- };
- }
- }
- if (!legendPosition) return;
- important(found, 'position', 'fixed');
- important(found, 'left', `${legendPosition.left}px`);
- important(found, 'top', `${legendPosition.top}px`);
- important(found, 'right', 'auto');
- important(found, 'bottom', 'auto');
- important(found, 'width', `${legendPosition.width}px`);
- important(found, 'margin', '0');
- important(found, 'transform', 'none');
- important(found, 'transition', 'none');
- important(found, 'z-index', '1000');
- }
- // ============================================
- // 2. ANNOTATIONS HEIGHT LOCK
- // ============================================
- let lockedHeight = null;
- function updateAnnotationsHeight() {
- if (lockedHeight !== null) return;
- if (editingActive()) return;
- const tab = document.querySelector(
- `${ANNOTATIONS_PANEL} ${ANNOTATIONS_TAB}`
- );
- if (!tab) return;
- const height = tab.getBoundingClientRect().height;
- if (height < 50) return;
- lockedHeight = height;
- const style = document.createElement('style');
- style.id = 'ew-ui-guard-annotations-height';
- style.textContent = `
- ${ANNOTATIONS_PANEL} ${ANNOTATIONS_TAB} {
- height: ${height}px !important;
- min-height: ${height}px !important;
- max-height: ${height}px !important;
- flex: 0 0 ${height}px !important;
- }
- `;
- (document.head || document.documentElement)
- .appendChild(style);
- console.info(
- LOG,
- 'Annotations height locked:',
- height
- );
- }
- // ============================================
- // 3. STATIONARY, FUNCTIONAL EYEWIRE RADIO
- //
- // UNCHANGED FROM WORKING v2.0.
- // ============================================
- function installRadioFix() {
- if (
- document.getElementById(
- 'ew-ui-guard-radio-position'
- )
- ) {
- return;
- }
- const style = document.createElement('style');
- style.id = 'ew-ui-guard-radio-position';
- style.textContent = `
- .nge-radio {
- bottom: 6px !important;
- transition: none !important;
- }
- `;
- (document.head || document.documentElement)
- .appendChild(style);
- }
- // ============================================
- // 4. SELECTION DETAILS
- // ============================================
- let selectionLocked = false;
- let selectionLocation = null;
- let manualOpenUntil = 0;
- try {
- selectionLocked =
- localStorage.getItem(STORAGE_KEY) === 'true';
- } catch (e) {
- console.warn(LOG, e);
- }
- function saveSelectionLock(value) {
- selectionLocked = value;
- try {
- localStorage.setItem(
- STORAGE_KEY,
- String(value)
- );
- } catch (e) {
- console.warn(LOG, e);
- }
- }
- function findSelectionLocation() {
- const manager = window.viewer?.sidePanelManager;
- if (!manager?.registeredPanels) return null;
- const entries = [...manager.registeredPanels];
- if (
- selectionLocation &&
- entries.some(
- e => e.location === selectionLocation
- )
- ) {
- return selectionLocation;
- }
- selectionLocation = null;
- const open = entries.find(
- e => e.panel?.element?.matches?.(SELECTION_PANEL)
- );
- if (open) {
- selectionLocation = open.location;
- return selectionLocation;
- }
- const matches = entries.filter(e => {
- const v = e.location?.value;
- return (
- v?.side === 'right' &&
- v?.row === 1 &&
- v?.minSize === 150
- );
- });
- if (matches.length === 1) {
- selectionLocation = matches[0].location;
- }
- return selectionLocation;
- }
- function protectSelectionVisibility() {
- const location = findSelectionLocation();
- if (!location || location.__ewGuardProtected) {
- return;
- }
- const descriptor =
- Object.getOwnPropertyDescriptor(
- Object.getPrototypeOf(location),
- 'visible'
- );
- if (
- typeof descriptor?.get !== 'function' ||
- typeof descriptor?.set !== 'function'
- ) {
- return;
- }
- Object.defineProperty(location, 'visible', {
- configurable: true,
- enumerable: descriptor.enumerable,
- get() {
- return descriptor.get.call(this);
- },
- set(value) {
- if (
- value === true &&
- selectionLocked &&
- performance.now() >= manualOpenUntil
- ) {
- return;
- }
- descriptor.set.call(this, value);
- }
- });
- Object.defineProperty(
- location,
- '__ewGuardProtected',
- {
- value: true,
- configurable: true
- }
- );
- }
- function selectionAction(event) {
- if (!event.isTrusted) return;
- if (!(event.target instanceof Element)) return;
- const settings =
- event.target.closest(SETTINGS_BUTTON);
- if (
- settings &&
- /selection\s*details/i.test(
- settings.textContent || ''
- )
- ) {
- saveSelectionLock(false);
- manualOpenUntil = performance.now() + 2000;
- return;
- }
- if (
- event.target.closest(
- `${SELECTION_PANEL} ${CLOSE_BUTTON}`
- )
- ) {
- saveSelectionLock(true);
- manualOpenUntil = 0;
- }
- }
- document.addEventListener(
- 'pointerdown',
- selectionAction,
- true
- );
- document.addEventListener(
- 'click',
- selectionAction,
- true
- );
- document.addEventListener(
- 'keydown',
- event => {
- if (
- event.key === 'Enter' ||
- event.key === ' '
- ) {
- selectionAction(event);
- }
- },
- true
- );
- // ============================================
- // 5. EXPERIMENTAL CUT/MERGE LAYER GUARD
- //
- // The uploaded trace identified two distinct
- // selection paths:
- //
- // Automatic editing activation:
- // activate -> selectedLayer.layer = pni_mec
- //
- // Automatic editing cleanup:
- // activation callback -> previous layer
- //
- // Manual layer selection uses a different
- // call stack.
- //
- // We intercept only the activation assignment
- // to the segmentation layer.
- //
- // No layer clicking or restoration is done.
- // ============================================
- let protectedSelectedLayer = null;
- const SEGMENTATION_LAYER = 'pni_mec';
- // Based on the exact activation location
- // identified in the uploaded trace.
- const ACTIVATION_CALL =
- /(?:^|\n)activate@.*main\.bundle\.js[^:\n]*:2167:150326(?:\n|$)/;
- function isAutomaticEditingActivation() {
- const stack = new Error().stack || '';
- return ACTIVATION_CALL.test(stack);
- }
- function installLayerGuard() {
- const selected = window.viewer?.selectedLayer;
- if (!selected) return;
- if (selected === protectedSelectedLayer) {
- return;
- }
- let owner = selected;
- let descriptor = null;
- while (owner && !descriptor) {
- descriptor =
- Object.getOwnPropertyDescriptor(
- owner,
- 'layer'
- );
- if (!descriptor) {
- owner = Object.getPrototypeOf(owner);
- }
- }
- if (
- !descriptor?.get ||
- !descriptor?.set ||
- !descriptor.configurable
- ) {
- return;
- }
- // Do not replace a non-configurable own property.
- const ownDescriptor =
- Object.getOwnPropertyDescriptor(
- selected,
- 'layer'
- );
- if (ownDescriptor && !ownDescriptor.configurable) {
- return;
- }
- const originalGet = descriptor.get;
- const originalSet = descriptor.set;
- Object.defineProperty(selected, 'layer', {
- configurable: true,
- enumerable: descriptor.enumerable,
- get() {
- return originalGet.call(this);
- },
- set(value) {
- const current = originalGet.call(this);
- const fromName = current?.name ?? null;
- const toName = value?.name ?? null;
- const unwantedActivation =
- current !== value &&
- fromName !== SEGMENTATION_LAYER &&
- toName === SEGMENTATION_LAYER &&
- editingActive() &&
- isAutomaticEditingActivation();
- if (unwantedActivation) {
- console.info(
- LOG,
- 'Blocked automatic editing selection:',
- fromName,
- '->',
- toName
- );
- return;
- }
- return originalSet.call(this, value);
- }
- });
- protectedSelectedLayer = selected;
- console.info(
- LOG,
- 'Experimental Cut/Merge layer guard installed'
- );
- }
- // ============================================
- // 6. START
- // ============================================
- function update() {
- try {
- updateLegend();
- updateAnnotationsHeight();
- protectSelectionVisibility();
- installLayerGuard();
- } catch (error) {
- console.warn(
- LOG,
- 'Update failed:',
- error
- );
- }
- }
- function start() {
- installRadioFix();
- update();
- setInterval(update, 100);
- console.info(LOG, 'v2.1 initialized');
- }
- if (document.readyState === 'loading') {
- document.addEventListener(
- 'DOMContentLoaded',
- start,
- { once: true }
- );
- } else {
- start();
- }
- })();
Advertisement
Add Comment
Please, Sign In to add comment