Skip to content

Commit da8be53

Browse files
committed
columns and annotator fix
1 parent 11ec02f commit da8be53

2 files changed

Lines changed: 79 additions & 90 deletions

File tree

components/annotorious-annotator/line-parser.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,14 @@ class AnnotoriousAnnotator extends HTMLElement {
335335
eraseTool.addEventListener("change", (e) => this.toggleErasingMode(e))
336336
seeTool.addEventListener("change", (e) => this.toggleAnnotationVisibility(e))
337337
createColumnsBtn.addEventListener("click", () =>
338-
window.location.href = `../../components/create-column/?annotationPage=${TPEN.RERUMURL}/id/${this.#annotationPageID}&projectID=${TPEN.activeProject._id}`
338+
window.location.href = `../../components/create-column/?projectID=${TPEN.activeProject._id}&pageID=${this.#annotationPageID}`
339339
)
340340
saveButton.addEventListener("click", (e) => {
341341
this.#annotoriousInstance.cancelSelected()
342342
// Timeout required in order to allow the unfocus native functionality to complete for $isDirty.
343343
setTimeout(() => { this.saveAnnotations() }, 500)
344344
})
345-
deleteAllBtn.addEventListener("click", (e) => this.deleteAllAnnotations(e))
345+
deleteAllBtn.addEventListener("click", async (e) => await this.deleteAllAnnotations(e))
346346
window.addEventListener('beforeunload', () => {
347347
if (this.#resolvedAnnotationPage?.$isDirty) {
348348
if (!confirm("If you leave unsaved changes will be lost.")){
@@ -993,10 +993,33 @@ class AnnotoriousAnnotator extends HTMLElement {
993993
* Use Annotorious to delete all known Annotations
994994
* https://annotorious.dev/api-reference/openseadragon-annotator/#clearannotations
995995
*/
996-
deleteAllAnnotations() {
996+
async deleteAllAnnotations() {
997997
this.#annotoriousInstance.clearAnnotations()
998998
this.#resolvedAnnotationPage.$isDirty = true
999-
this.saveAnnotations()
999+
await this.saveAnnotations()
1000+
try {
1001+
await this.clearColumnsServerSide()
1002+
} catch (err) {
1003+
console.error("Could not clear columns server side.", err)
1004+
}
1005+
}
1006+
1007+
/**
1008+
* Call TPEN Services to clear all columns server side.
1009+
*/
1010+
async clearColumnsServerSide() {
1011+
try {
1012+
const res = await fetch(`${TPEN.servicesURL}/project/${TPEN.activeProject._id}/page/${this.#annotationPageID}/clear-columns`, {
1013+
method: 'DELETE',
1014+
headers: {
1015+
"Authorization": `Bearer ${TPEN.getAuthorization()}`,
1016+
'Content-Type': 'application/json'
1017+
}
1018+
})
1019+
if (!res.ok) throw new Error(`Server error: ${res.status}`)
1020+
} catch (err) {
1021+
throw err
1022+
}
10001023
}
10011024

10021025
/**

components/create-column/index.js

Lines changed: 52 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ class TpenCreateColumn extends HTMLElement {
77
TPEN.attachAuthentication(this)
88

99
this.projectID = null
10+
this.pageID = null
1011
this.annotationPageID = null
1112
this.selectedBoxes = []
1213
this.lastClickedIndex = null
1314
this.totalIds = []
1415
this.existingColumns = []
16+
this.originalColumnLabels = []
1517

1618
this.shadowRoot.innerHTML = `
1719
<style>
@@ -33,10 +35,10 @@ class TpenCreateColumn extends HTMLElement {
3335
transition: box-shadow 0.15s ease, border-color 0.15s ease;
3436
border-radius: 2px;
3537
display: flex;
36-
align-items: center;
38+
align-items: flex-start;
3739
justify-content: flex-start;
3840
padding-left: 10px;
39-
font-size: 16px;
41+
font-size: 12px;
4042
}
4143
.overlayBox.clicked {
4244
border-color: red;
@@ -174,8 +176,7 @@ class TpenCreateColumn extends HTMLElement {
174176
border-radius: 5px;
175177
transition: all 0.3s;
176178
cursor: pointer;
177-
width: 20%;
178-
font-size: 14px;
179+
font-size: 12px;
179180
font-weight: 600;
180181
}
181182
.merge-label-btn:hover, .extend-label-btn:hover {
@@ -223,20 +224,33 @@ class TpenCreateColumn extends HTMLElement {
223224
connectedCallback() {
224225
localStorage.removeItem('annotationsState')
225226
const params = new URLSearchParams(window.location.search)
226-
const annotationPage = params.get("annotationPage")
227+
this.pageID = `${TPEN.RERUMURL}/id/${params.get("pageID")}`
227228
this.projectID = params.get("projectID")
228-
this.annotationPageID = annotationPage.split("/").pop()
229-
TPEN.eventDispatcher.on('tpen-project-loaded', () => this.loadPage(annotationPage))
229+
this.annotationPageID = this.pageID.split("/").pop()
230+
TPEN.eventDispatcher.on('tpen-project-loaded', async () => await this.loadPage(this.pageID))
231+
}
232+
233+
async columnLabelCheck() {
234+
this.originalColumnLabels = this.existingColumns.map(col => col.label)
235+
this.existingColumns = this.existingColumns.map((col, index) => {
236+
const { label } = col
237+
const isAutoLabel = label.startsWith("Column ") && /^[a-f\d]{24}$/i.test(label.slice(7))
238+
return {
239+
...col,
240+
label: isAutoLabel ? `Unnamed ${index + 1}` : label
241+
}
242+
})
230243
}
231244

232-
async loadPage(annotationPage = null) {
245+
async loadPage(pageID = null) {
233246
try {
234247
this.showLoading()
235-
let { imgUrl, annotations, imgWidth, imgHeight } = await this.fetchPageViewerData(annotationPage)
248+
let { imgUrl, annotations, imgWidth, imgHeight } = await this.fetchPageViewerData(pageID)
236249
await this.renderImage(imgUrl)
237250
const page = await TPEN.activeProject.layers.flatMap(layer => layer.pages || []).find(p => p.id.split('/').pop() === this.annotationPageID)
238251
this.existingColumns = page?.columns?.map(column => ({ label: column.label, lines: column.lines })) || []
239252
const assignedAnnotationIds = []
253+
await this.columnLabelCheck()
240254
this.existingColumns.forEach(column => {
241255
column.lines.forEach(annoId => assignedAnnotationIds.push({
242256
lineid: annoId, columnLabel: column.label
@@ -332,11 +346,11 @@ class TpenCreateColumn extends HTMLElement {
332346
if(btn.style.backgroundColor !== 'white') {
333347
btn.style.backgroundColor = 'white'
334348
btn.style.color = 'var(--primary-color)'
335-
columnLabelsToMerge.push(label)
349+
columnLabelsToMerge.push(columnLabels.indexOf(label) !== -1 ? columnLabels.indexOf(label) : '')
336350
} else {
337351
btn.style.backgroundColor = 'var(--primary-color)'
338352
btn.style.color = 'white'
339-
columnLabelsToMerge.pop(label)
353+
columnLabelsToMerge.pop(columnLabels.indexOf(label) !== -1 ? columnLabels.indexOf(label) : '')
340354
}
341355
})
342356
workspaceToolbar.appendChild(btn)
@@ -376,30 +390,14 @@ class TpenCreateColumn extends HTMLElement {
376390
},
377391
body: JSON.stringify({
378392
newLabel,
379-
columnLabelsToMerge
393+
columnLabelsToMerge: columnLabelsToMerge.map(index => this.originalColumnLabels[index]),
380394
})
381395
})
382396
if (!res.ok) throw new Error(`Failed to merge columns: ${res.status}`)
383397
TPEN.eventDispatcher.dispatch("tpen-toast", {
384398
status: "success", message: 'Columns merged successfully.'
385399
})
386-
this.shadowRoot.querySelectorAll('.overlayBox').forEach(box => {
387-
if (columnLabelsToMerge.includes(box.textContent)) {
388-
box.classList.add('disabled')
389-
box.textContent = newLabel
390-
}
391-
})
392-
this.existingColumns = this.existingColumns.filter(col => !columnLabelsToMerge.includes(col.label))
393-
this.existingColumns.push({ label: newLabel, lines: this.selectedBoxes.map(b => b.dataset.lineid) })
394-
this.selectedBoxes = []
395-
this.lastClickedIndex = null
396-
this.totalIds = this.totalIds.filter(id => !this.selectedBoxes.some(b => b.dataset.lineid === id))
397-
localStorage.setItem('annotationsState', JSON.stringify({ remainingIDs: this.totalIds, selectedIDs: [] }))
398-
workspaceToolbar.innerHTML = `
399-
<h2 class="workspace-title">Workspace</h2>
400-
<p class="workspace-description">Use this area to merge existing columns and extending them.</p>
401-
`
402-
workspaceToolbar.style.justifyContent = 'flex-start'
400+
window.location.reload()
403401
} catch (error) {
404402
TPEN.eventDispatcher.dispatch("tpen-toast", {
405403
status: "error", message: error.message
@@ -434,15 +432,22 @@ class TpenCreateColumn extends HTMLElement {
434432
btn.style.padding = '8px 12px'
435433
btn.style.cursor = 'pointer'
436434
btn.addEventListener('click', () => {
437-
columnToExtend = label
438-
Array.from(workspaceToolbar.querySelectorAll('.extend-label-btn')).forEach(otherBtn => {
439-
if (otherBtn !== btn) {
440-
otherBtn.style.backgroundColor = 'var(--primary-color)'
441-
otherBtn.style.color = 'var(--white)'
442-
}
443-
})
444-
btn.style.backgroundColor = 'white'
445-
btn.style.color = 'var(--primary-color)'
435+
if (btn.style.backgroundColor === 'white') {
436+
columnToExtend = ''
437+
btn.style.backgroundColor = 'var(--primary-color)'
438+
btn.style.color = 'var(--white)'
439+
}
440+
else {
441+
columnToExtend = columnLabels.indexOf(label) !== -1 ? columnLabels.indexOf(label) : ''
442+
Array.from(workspaceToolbar.querySelectorAll('.extend-label-btn')).forEach(otherBtn => {
443+
if (otherBtn !== btn) {
444+
otherBtn.style.backgroundColor = 'var(--primary-color)'
445+
otherBtn.style.color = 'var(--white)'
446+
}
447+
})
448+
btn.style.backgroundColor = 'white'
449+
btn.style.color = 'var(--primary-color)'
450+
}
446451
})
447452
workspaceToolbar.appendChild(btn)
448453
})
@@ -454,7 +459,7 @@ class TpenCreateColumn extends HTMLElement {
454459
workspaceToolbar.appendChild(extendColumnBtn)
455460

456461
extendColumnBtn.addEventListener('click', async () => {
457-
if (!columnToExtend) {
462+
if (!this.originalColumnLabels[columnToExtend]) {
458463
return TPEN.eventDispatcher.dispatch("tpen-toast", {
459464
status: "error", message: 'Please select a column to extend.'
460465
})
@@ -475,27 +480,15 @@ class TpenCreateColumn extends HTMLElement {
475480
"Content-Type": "application/json"
476481
},
477482
body: JSON.stringify({
478-
columnLabel: columnToExtend,
483+
columnLabel: this.originalColumnLabels[columnToExtend],
479484
annotationIdsToAdd
480485
})
481486
})
482487
if (!res.ok) throw new Error(`Failed to extend column: ${res.status}`)
483488
TPEN.eventDispatcher.dispatch("tpen-toast", {
484489
status: "success", message: 'Column extended successfully.'
485490
})
486-
this.selectedBoxes.forEach(b => {
487-
b.classList.add('disabled')
488-
b.textContent = columnToExtend
489-
})
490-
this.selectedBoxes = []
491-
this.lastClickedIndex = null
492-
this.totalIds = this.totalIds.filter(id => !this.selectedBoxes.some(b => b.dataset.lineid === id))
493-
localStorage.setItem('annotationsState', JSON.stringify({ remainingIDs: this.totalIds, selectedIDs: [] }))
494-
workspaceToolbar.innerHTML = `
495-
<h2 class="workspace-title">Workspace</h2>
496-
<p class="workspace-description">Use this area to merge existing columns and extending them.</p>
497-
`
498-
workspaceToolbar.style.justifyContent = 'flex-start'
491+
window.location.reload()
499492
} catch (error) {
500493
TPEN.eventDispatcher.dispatch("tpen-toast", {
501494
status: "error", message: error.message
@@ -529,15 +522,15 @@ class TpenCreateColumn extends HTMLElement {
529522
async getSpecificTypeData(type) {
530523
if (!type) throw new Error("No IIIF resource provided")
531524
if (typeof type === "string" && this.isValidUrl(type)) {
532-
const res = await fetch(type)
525+
const res = await fetch(type, { cache: "no-store" })
533526
if (!res.ok) throw new Error(`Fetch failed: ${res.status}`)
534527
return await res.json()
535528
} else if (typeof type === "object" && this.isValidJSON(type)) return type
536529
else throw new Error("Invalid IIIF input")
537530
}
538531

539-
async fetchPageViewerData(annotationPage) {
540-
const annotationPageData = annotationPage ? await this.getSpecificTypeData(annotationPage) : null
532+
async fetchPageViewerData(pageID = null) {
533+
const annotationPageData = pageID ? await this.getSpecificTypeData(pageID) : null
541534
const canvasData = await this.getSpecificTypeData(annotationPageData.target)
542535
return await this.processDirectCanvasData(canvasData, annotationPageData)
543536
}
@@ -552,7 +545,7 @@ class TpenCreateColumn extends HTMLElement {
552545
if (!annotationPageData?.items) return []
553546
const results = await Promise.all(annotationPageData.items.map(async anno => {
554547
try {
555-
const res = await fetch(anno.id)
548+
const res = await fetch(anno.id, { cache: "no-store" })
556549
const data = await res.json()
557550
return { target: data?.target?.selector?.value ?? data?.target, lineid: data?.id }
558551
} catch { return null }
@@ -726,20 +719,10 @@ class TpenCreateColumn extends HTMLElement {
726719

727720
if (!res.ok)
728721
throw new Error(`Server error: ${res.status}`)
729-
this.selectedBoxes.forEach(b => {
730-
b.classList.add('disabled')
731-
b.textContent = columnLabel
732-
})
733-
this.existingColumns.push({ label: columnLabel, lines: selectedIDs })
734-
this.totalIds = this.totalIds.filter(id => !this.selectedBoxes.some(b => b.dataset.lineid === id))
735-
localStorage.setItem('annotationsState', JSON.stringify({ remainingIDs: this.totalIds, selectedIDs: [] }))
736-
737-
this.selectedBoxes = []
738-
this.lastClickedIndex = null
739-
this.shadowRoot.getElementById("columnTitle").value = ""
740722
TPEN.eventDispatcher.dispatch("tpen-toast", {
741723
status: "info", message: 'Column created successfully.'
742724
})
725+
window.location.reload()
743726
} catch (err) {
744727
console.error("Column creation failed:", err)
745728
TPEN.eventDispatcher.dispatch("tpen-toast", {
@@ -758,27 +741,10 @@ class TpenCreateColumn extends HTMLElement {
758741
}
759742
})
760743
if (!res.ok) throw new Error(`Server error: ${res.status}`)
761-
const boxes = Array.from(this.shadowRoot.querySelectorAll('.overlayBox'))
762-
boxes.forEach(b => {
763-
b.classList.remove('clicked','disabled')
764-
b.textContent = ''
765-
})
766-
this.selectedBoxes = []
767-
this.lastClickedIndex = null
768-
this.existingColumns = []
769-
const params = new URLSearchParams(window.location.search)
770-
const annotationPage = params.get("annotationPage")
771-
let { annotations } = await this.fetchPageViewerData(annotationPage)
772-
this.totalIds = annotations.map(a => a.lineid)
773-
774-
localStorage.setItem('annotationsState', JSON.stringify({
775-
remainingIDs: this.totalIds,
776-
selectedIDs: []
777-
}))
778-
779744
TPEN.eventDispatcher.dispatch("tpen-toast", {
780745
status: "info", message: 'All columns cleared successfully.'
781746
})
747+
window.location.reload()
782748
} catch (err) {
783749
console.error("Failed to clear columns:", err)
784750
TPEN.eventDispatcher.dispatch("tpen-toast", {

0 commit comments

Comments
 (0)