[script] Dock (mostly for programmers)

I’ve rebuilt the Dock.dialog() method a little bit. Now the dialog has these options available:

let dialog = Dock.dialog({
  html: '',
  id: '',
  css: '',
  okCallback: null,
  cancelCallback: null,
  okLabel: 'OK',
  cancelLabel: 'Cancel',
  afterCreateCallback: null,
  destroyAfterClosing: false,
  width: 200
})

Most of the options are the same, as in the previous version, but the new ones are:
afterCreateCallback - which allows you to, e.g. assign events to nodes in the dialog
destroyAfterClosing - if set to true removes the whole dialog node from the DOM tree, so if you hide() the dialog and try to show() it again, it want be posssible
width - self explanatory.
There’s also an overlay behind each dialog. The overlay blocks user from interacting with the background while the dialog is opened. Clicking on the overlay will have the same effect as closing the dialog via Cancel button.

Added new static method called getShareableUrl().
It returns an URL, which you can share with others. Basically, same as the Share button, but now you can generate your own links, without calling the Share dialog.

Added new static method called getRootIdByCurrentCoords(callback).
It’s just a shortcut for getting segment by coords and the getting root by segment.

The fetch event is now also called, when the fetch() method was aborted. In both cases (success and abort) the event.detail now contains a boolean field called success and in the case of abort, there’s also a field called message.
You don’t have to .catch() errors manually, just check the success field and act accordingly.

Method .getRootId() now returns an AbortController, so you can abort your request, if you want, e.g.:

let request = Dock.getRootId('123456', rootId => console.log(rootId))
request.abort() // the request won't be continued after this line

I wanted to do it also for .getSegmentId(), but this method uses a link from a different domain, so I had to use GM_xmlhttpRequest() function (with appropriate grants) and it doesn’t have any way to be aborted (AFAIK).

1 Like

Dock.getCurrentMouseCoords() and Dock.getCurrentCoords() are now based on internal values, not on the UI.

Added Dock.getRootIdByCoords(x, y, z, callback) which is just a short form of calling Dock.getSegmentId() and then Dock.getRootId().

Fixed an error causing, that addons’ positions weren’t exactly the same, as they were, when a user positioned them. There was a difference between measuring the position and positioning addon. The difference was, that one was taking into account margins and the other - not.

Dock.getSegmentId() now also accepts arrays of coordinates:

const x = [1, 2, 3]
const y = [1, 5, 10]
const z = [3, 2, 8]
Dock.getSegmentId(x, y, z, rootIds => {
  // rootIds is an array
})

The arrays are constructed this way (not as array of triplets), because that is the expected way from the ITANNA API.
If x, y and z are single numbers, the argument for the callback will still be a single value (string containing the rootId).

Dock.getRootId() now has third argument - a boolean value. By default it’s false. If set to true the method will return a Promise, not an AbortController.
In such case you have to handle the result by yourself.
It’s useful, when you want to find more root IDs and run Promise.all() on all the calls.

1 Like

Added Dock.mergeObjects(target, source) method.
It allows deep merging of objects. It doesn’t deal with circular references, so be careful with that.
I’ve made it a couple of months ago to merge object literals containing nested settings and it was used in the Presets addon.
Now I had a new use for it (in Utilities) so decided to move it to the Dock to make the code a little bit DRYer :wink:

It merges objects containing other objects, arrays and primitives.
If both source and target contain the same key with a primitive value, the value from the source will be written over the target value. If key only exists in the source or in the target, it will exists in the target.
If key isn’t a primitve, it’s values are looped over and primitives will be overwritten as above. Objects and arrays will be analyzed key-by-key.
The result will be in the target object.

1 Like

I haven’t updated the changelog for a while, so here are the changes since the last post:

Added the Sifrr-storage library here (moved it from the Links addon, because the Names addon now also uses it). It’s a wrapper to make easier use of the IndexedDB and other storage methods.
If you’re using the Dock, the single thing, you have to do to initialize an IndexedDB storage, is:

const storage = window.Sifrr.Storage.getStorage('indexeddb')

You can (and should) also pass a name of the database. After that, you simply use .get(), .set() and .remove() methods on the storage object. It’s s Promise based library, so you have to use .then() to read the results. Take a look at the project’s GitHub page for more details.

Added the Uint64 class. Actually, I’ve just copied it from the Neuroglancer source code removing the TS parts on the way. I did it so one doesn’t have to create a TS project and import the module. It’s now a global object, so, to create a new Uint64 object, you should just call:

let my64Int = new Uint64() // my64Int.low = 0; my64Int.high = 0
let myOther64Int = new Uint64(10, 50) // first argument is the low part and the other - high part of the number

Added addToRightTab() method.
This method allows you to add your code to any of the right panel tabs, whenever that tab is accessible (the tabs are dynamically created, so not all of them are available right after the start of the site).
To use it, call the method this way:

const callback = () => console.log('works')
Dock.addToRightTab('segmentation_with_graph', 'graph', callback, selector)

The first parameter is the type of the main tab (the ones at the top). The available types are: image, segmentation, segmentation_with_graph and annotation.
The second - the name of the tab on the right (lower-cased).
The third - a function containing the code, you want to be run, when that specific tab exists in the DOM and is visible.
The fourth - optional. When added, the method will check, if an element with this selector exists. If no, then the code will be executed. That means, that the code in callback can be executed multiple times - every time, when the target tab disappears/refreshes during working.
The code will be run only once, because the main use for this method is to add various nodes and callbacks.

1 Like

Fixed the .addToRightTab() method from the previous post. Now you don’t have to add a selector at the end, to monitor, when a tab has ben recreated. Now the method itself monitors such changes and when it sees, that the tab has been removed and created anew, the callback parameter will be called again.

Added .addToMainTab() method. It has only 2 arguments - type of a tab and a callback. It works similarly to the .addToRightTab() method, but monitors the main tabs (images, segmentation, annotations, etc.)

Added .getRandomColor() static method to generate a string starting with ‘#’ followed by 6 hexadecimal numbers.

Added .arraySubtraction(arr1, arr2) static method to subtract from arr1 elements, that exist in the arr2. Result is returned as a new array. (fun fact: this method was generated for me by ChatGPT, because I was too lazy at that moment, to do it by myself :smiley: ).

Added .generateChunkToSegMap() static method, that returns a map with segment id associated to each of the chunks’ ids. Work only for already loaded segments.

Added .cacheFragments() static method. It expects an array of ids and load all fragments/chunks associatied with each segment to the local memory.

Dock now triggers dock-ready event, when it has been successfully loaded, so one doesn’t have to check if dockIsReady global variable exists in an interval loop to start their own script (this method still works though).

1 Like