Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev/big_viewport.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"width": 1000,
"height": 1000,
"url": "https://www.encodeproject.org/files/ENCFF799QGA/@@download/ENCFF799QGA.hic",
"name": "in-situ agar GM12878 MboI experiment",
// "name": "in-situ agar GM12878 MboI experiment",
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Property "name" is commented out but should either be removed entirely or have an explanation for why it's commented out. Leaving commented-out code reduces maintainability.

Suggested change
// "name": "in-situ agar GM12878 MboI experiment",

Copilot uses AI. Check for mistakes.
"tracks": [
{
"url": "https://www.dropbox.com/s/gvfvcvt146rtm5b/GSE63525_GM12878_subcompartments.bed?dl=0",
Expand Down
2 changes: 1 addition & 1 deletion dev/dat-sequence-gene-track.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
],
}

juicebox.init(document.getElementById("app-container"), config_deep_map)
juicebox.init(document.getElementById("app-container"), config)
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable config_deep_map is changed to config, but config is not defined in this file. This will cause a ReferenceError at runtime. Either define config or revert to config_deep_map.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

.then(browser => {

console.log(`${ browser.id } is good to go`)
Expand Down
159 changes: 159 additions & 0 deletions docs/EXTERNAL_PROJECT_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Integrating External Projects with Juicebox.js

This document describes how to work with external projects (like Spacewalk) that use Juicebox.js.

## Multi-Root Workspace (Current Approach)

**We use a multi-root workspace to reference Spacewalk without copying files.**

### Setup:
1. Ensure Spacewalk is cloned locally (e.g., as a sibling directory: `../spacewalk`)
2. Open `juicebox-with-spacewalk.code-workspace` in Cursor/VS Code
3. Both projects will appear in the file explorer side-by-side

### Benefits:
- ✅ Full code navigation and IntelliSense across both projects
- ✅ Can search across both codebases
- ✅ No file duplication
- ✅ Always sees latest Spacewalk changes immediately
- ✅ Git history stays separate
- ✅ Cursor AI can understand both codebases together

### Usage:
```bash
# Open the workspace file in Cursor
cursor juicebox-with-spacewalk.code-workspace

# Or in VS Code
code juicebox-with-spacewalk.code-workspace
```

### Updating the Spacewalk Path:
If your Spacewalk repository is in a different location, edit `juicebox-with-spacewalk.code-workspace` and update the path:
```json
{
"name": "Spacewalk (External Reference)",
"path": "/absolute/path/to/spacewalk" // or "../relative/path/to/spacewalk"
}
```

## Option 2: Git Submodules

**Best for:** When Spacewalk is a separate Git repository

### Setup:
```bash
# Add Spacewalk as a submodule
git submodule add <spacewalk-repo-url> spacewalk-reference

# Or if Spacewalk is already a submodule elsewhere
git submodule add <spacewalk-repo-url> external/spacewalk
```

### Benefits:
- ✅ Tracks specific commit of Spacewalk
- ✅ Can update to latest version easily
- ✅ Keeps projects separate
- ✅ Works well with CI/CD

### Drawbacks:
- ⚠️ Requires submodule initialization (`git submodule update --init`)
- ⚠️ Can be confusing for team members

## Option 3: Symbolic Links

**Best for:** Quick local development (not recommended for Git)

### Setup:
```bash
# Create a symlink to Spacewalk's juiceboxPanel.js
ln -s /path/to/spacewalk/src/panels/juiceboxPanel.js spacewalk-code/juiceboxPanel.js
```

### Benefits:
- ✅ Always points to latest version
- ✅ No duplication

### Drawbacks:
- ⚠️ Symlinks don't work well in Git (platform-specific)
- ⚠️ Can break if Spacewalk moves
- ⚠️ Not portable across machines

## Option 4: npm/yarn Workspaces (If Both Are Packages)

**Best for:** When both projects are npm packages

### Setup:
Create a root `package.json`:
```json
{
"name": "juicebox-workspace",
"private": true,
"workspaces": [
".",
"../spacewalk"
]
}
```

### Benefits:
- ✅ Shared dependencies
- ✅ Can link packages locally
- ✅ Standard npm workflow

## Option 5: GitHub Integration

**Best for:** Reference code from GitHub without cloning

### Using GitHub CLI:
```bash
# View file from GitHub repo
gh repo view <owner>/spacewalk --web

# Or use GitHub's web interface to browse
```

### Using Cursor's GitHub Integration:
- Cursor can reference GitHub repos in some contexts
- Use GitHub URLs in comments/links
- Not as seamless as local files

## Option 6: Documentation-Based Approach

**Best for:** When you just need to document usage patterns

### Create a reference file:
```markdown
# Spacewalk Integration Reference

See: https://github.com/your-org/spacewalk/blob/main/src/panels/juiceboxPanel.js

Key integration points:
- Line 82: `hic.restoreSession()`
- Line 89: `hic.getCurrentBrowser()`
- etc.
```

## Current Setup

We use the **Multi-Root Workspace** approach. The workspace file (`juicebox-with-spacewalk.code-workspace`) includes both:
- The juicebox.js project (current directory)
- The Spacewalk project (external reference)

This allows Cursor to understand both codebases together, making it easy to:
- Check compatibility when refactoring Juicebox.js
- See how Spacewalk uses Juicebox.js APIs
- Navigate between both projects seamlessly
- Get IntelliSense and code completion across both

### Notes:
- The workspace file is committed to Git, but Spacewalk itself is not
- Each developer needs to have Spacewalk cloned locally
- Update the path in the workspace file if Spacewalk is in a different location

## Questions?

- For workspace setup: See Cursor/VS Code documentation on multi-root workspaces
- For Git submodules: See `git help submodule`
- For npm workspaces: See npm/yarn workspace documentation

2 changes: 1 addition & 1 deletion examples/juicebox-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<!-- Juicebox CSS-->
<!-- <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/juicebox.js@2.4.8/dist/css/juicebox.css"> -->
<link rel="stylesheet" type="text/css" href="../dist/css/juicebox.css">
<link rel="stylesheet" href="../css/juicebox.css">

<!-- Juicebox js -- defines global "juicebox" -->
<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/juicebox.js@2.4.8/dist/juicebox.min.js"></script> -->
Expand Down
13 changes: 5 additions & 8 deletions examples/juicebox-minimal.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css">

<!-- Juicebox CSS-->
<link rel="stylesheet" type="text/css" href="../dist/css/juicebox.css">
<link rel="stylesheet" href="../css/juicebox.css">

</head>

Expand All @@ -19,18 +19,15 @@

<script type="module">

import juicebox from "https://cdn.jsdelivr.net/npm/juicebox.js@2.4.8/dist/juicebox.esm.js"
import juicebox from '../js/index.js';

const config =
const config =
{
"url": "https://hicfiles.s3.amazonaws.com/hiseq/gm12878/dilution/combined.hic",
}


juicebox.init(document.getElementById("app-container"), config)
.then(browser => {
console.log(`${browser.id} is good to go`)
})
const browser = await juicebox.init(document.getElementById("app-container"), config)
console.log(`${browser.id} is good to go`)


</script>
Expand Down
4 changes: 2 additions & 2 deletions examples/juicebox.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css">

<!-- Juicebox CSS-->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/juicebox.js@2.4.8/dist/css/juicebox.css">
<link rel="stylesheet" href="../css/juicebox.css">

</head>

Expand All @@ -19,7 +19,7 @@

<script type="module">

import juicebox from "https://cdn.jsdelivr.net/npm/juicebox.js@2.4.8/dist/juicebox.esm.js"
import juicebox from '../js/index.js';

const config =
{
Expand Down
8 changes: 4 additions & 4 deletions js/annotationWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class AnnotationWidget {

if (isTrack2D) {
track.color = color;
this.browser.eventBus.post(HICEvent('TrackState2D', track));
this.browser.notifyTrackState2D(track);
} else {
trackRenderer.setColor(color);
}
Expand Down Expand Up @@ -227,7 +227,7 @@ class AnnotationWidget {
trackList[index] = temp;

if (isTrack2D) {
this.browser.eventBus.post(HICEvent('TrackState2D', trackList));
this.browser.notifyTrackState2D(trackList);
this.updateBody(trackList);
} else {
this.browser.updateLayout();
Expand All @@ -241,7 +241,7 @@ class AnnotationWidget {
trackList[index] = temp;

if (isTrack2D) {
this.browser.eventBus.post(HICEvent('TrackState2D', trackList));
this.browser.notifyTrackState2D(trackList);
this.updateBody(trackList);
} else {
this.browser.updateLayout();
Expand All @@ -263,7 +263,7 @@ class AnnotationWidget {
trackList.splice(index, 1);
this.browser.contactMatrixView.clearImageCaches();
this.browser.contactMatrixView.update();
this.browser.eventBus.post(HICEvent('TrackLoad2D', trackList));
this.browser.notifyTrackLoad2D(trackList);
} else {
this.browser.layoutController.removeTrackXYPair(track.x.track.trackRenderPair);
}
Expand Down
2 changes: 1 addition & 1 deletion js/browserUIManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BrowserUIManager {
this.components.set('resolutionSelector', new ResolutionSelector(this.browser, navContainer));
this.getComponent('resolutionSelector').setResolutionLock(this.browser.resolutionLocked);

this.components.set('colorScale', new ColorScaleWidget(this.browser, navContainer));
this.components.set('colorScaleWidget', new ColorScaleWidget(this.browser, navContainer));

this.components.set('controlMap', new ControlMapWidget(this.browser, navContainer));

Expand Down
29 changes: 22 additions & 7 deletions js/contactMatrixView.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ class ContactMatrixView {
if (state.normalization !== "NONE") {
if (!ds.hasNormalizationVector(state.normalization, zd.chr1.name, zd.zoom.unit, zd.zoom.binSize)) {
Alert.presentAlert(`Normalization option ${state.normalization} unavailable at this resolution.`);
this.browser.eventBus.post(new HICEvent("NormalizationExternalChange", "NONE"));
this.browser.notifyNormalizationExternalChange("NONE");
state.normalization = "NONE";
}
}
Expand Down Expand Up @@ -569,7 +569,7 @@ class ContactMatrixView {
const changed = this.colorScale.threshold !== this.colorScaleThresholdCache[colorKey]
this.colorScale.setThreshold(this.colorScaleThresholdCache[colorKey])
if (changed) {
this.browser.eventBus.post(HICEvent("ColorScale", this.colorScale))
this.browser.notifyColorScale(this.colorScale)
}
return this.colorScale
} else {
Expand All @@ -589,7 +589,7 @@ class ContactMatrixView {
this.colorScale = new ColorScale(this.colorScale)
this.colorScale.setThreshold(s)
this.computeColorScale = false
this.browser.eventBus.post(HICEvent("ColorScale", this.colorScale))
this.browser.notifyColorScale(this.colorScale)
this.colorScaleThresholdCache[colorKey] = s
}

Expand Down Expand Up @@ -758,7 +758,7 @@ class ContactMatrixView {
xy.xNormalized = xy.x / width;
xy.yNormalized = xy.y / height;

this.browser.eventBus.post(HICEvent("UpdateContactMapMousePosition", xy, false));
this.browser.notifyUpdateContactMapMousePosition(xy);

if (this.willShowCrosshairs) {
this.browser.updateCrosshairs(xy);
Expand Down Expand Up @@ -789,7 +789,7 @@ class ContactMatrixView {
this.isDragging = true;
const dx = mouseLast.x - coords.x;
const dy = mouseLast.y - coords.y;
this.browser.shiftPixels(dx, dy);
this.browser.shiftPixels(dx, dy).catch(err => console.error('Error in shiftPixels:', err));
}
mouseLast = coords;
}
Expand All @@ -805,6 +805,21 @@ class ContactMatrixView {
this.browser.zoomAndCenter(1, mouseX, mouseY);
})

viewportElement.addEventListener('wheel', (e) => {
e.preventDefault();
e.stopPropagation();

const zoomFactor = 0.008;
// deltaY > 0 means scroll down (zoom out), deltaY < 0 means scroll up (zoom in)
// For juicebox: scaleFactor > 1 = zoom in, scaleFactor < 1 = zoom out
const scaleFactor = e.deltaY > 0 ? 1 - zoomFactor : 1 + zoomFactor;
const anchorPx = e.offsetX;
const anchorPy = e.offsetY;

this.browser.interactions.handleWheelZoom(anchorPx, anchorPy, scaleFactor)
.catch(err => console.error('Error in handleWheelZoom:', err));
})

viewportElement.addEventListener('mouseover', () => mouseOver = true)
viewportElement.addEventListener('mouseout', () => mouseOver = undefined)

Expand Down Expand Up @@ -843,7 +858,7 @@ class ContactMatrixView {
height: Math.abs(currentY - startY)
};

this.sweepZoom.commit(sweepRect)
this.sweepZoom.commit(sweepRect).catch(err => console.error('Error in sweepZoom.commit:', err));
}
})
}
Expand Down Expand Up @@ -935,7 +950,7 @@ class ContactMatrixView {
const dy = lastTouch.y - offsetY;
if (!isNaN(dx) && !isNaN(dy)) {
this.isDragging = true;
this.browser.shiftPixels(dx, dy);
this.browser.shiftPixels(dx, dy).catch(err => console.error('Error in shiftPixels:', err));
}
}

Expand Down
22 changes: 22 additions & 0 deletions js/controlMapWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ class ControlMapWidget {
getDisplayModeCycle() {
return this.controlMapHash.cycleID;
}

/**
* Hide the control map widget container.
*/
hide() {
this.container.style.display = 'none';
}

/**
* Show the control map widget container.
*/
show() {
this.container.style.display = 'block';
}

/**
* Update the display mode options.
* @param {string} displayMode - The current display mode
*/
updateDisplayMode(displayMode) {
this.controlMapHash.updateOptions(displayMode);
}
}

class ControlMapHash {
Expand Down
Loading
Loading