| |
| |
| |
| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
|
|
| |
| |
| |
| class InstanceDisplayManager { |
| constructor() { |
| this.displayContainer = document.querySelector('.instance-display-container'); |
| this.displayFields = {}; |
| this.spanTargets = []; |
|
|
| if (this.displayContainer) { |
| this.init(); |
| } |
| } |
|
|
| |
| |
| |
| init() { |
| this.collectDisplayFields(); |
| this.initImageZoom(); |
| this.initCollapsibleSections(); |
| this.initSpanTargets(); |
| this.initPerTurnRatings(); |
|
|
| console.log('[InstanceDisplay] Initialized with', Object.keys(this.displayFields).length, 'fields'); |
| } |
|
|
| |
| |
| |
| collectDisplayFields() { |
| const fields = this.displayContainer.querySelectorAll('.display-field'); |
| fields.forEach(field => { |
| const key = field.dataset.fieldKey; |
| const type = field.dataset.fieldType; |
| if (key) { |
| this.displayFields[key] = { |
| element: field, |
| type: type, |
| isSpanTarget: field.dataset.spanTarget === 'true' |
| }; |
|
|
| if (field.dataset.spanTarget === 'true') { |
| this.spanTargets.push(key); |
| } |
| } |
| }); |
| } |
|
|
| |
| |
| |
| initImageZoom() { |
| const zoomContainers = document.querySelectorAll('.image-zoom-container'); |
| zoomContainers.forEach(container => { |
| const img = container.querySelector('img'); |
| const zoomIn = container.querySelector('.zoom-in'); |
| const zoomOut = container.querySelector('.zoom-out'); |
| const zoomReset = container.querySelector('.zoom-reset'); |
|
|
| if (!img) return; |
|
|
| let scale = 1; |
| const minScale = 0.5; |
| const maxScale = 5; |
| const scaleStep = 1.25; |
|
|
| const updateScale = (newScale) => { |
| scale = Math.max(minScale, Math.min(maxScale, newScale)); |
| img.style.transform = `scale(${scale})`; |
| img.style.transformOrigin = 'center center'; |
| }; |
|
|
| if (zoomIn) { |
| zoomIn.addEventListener('click', (e) => { |
| e.preventDefault(); |
| updateScale(scale * scaleStep); |
| }); |
| } |
|
|
| if (zoomOut) { |
| zoomOut.addEventListener('click', (e) => { |
| e.preventDefault(); |
| updateScale(scale / scaleStep); |
| }); |
| } |
|
|
| if (zoomReset) { |
| zoomReset.addEventListener('click', (e) => { |
| e.preventDefault(); |
| updateScale(1); |
| }); |
| } |
|
|
| |
| container.addEventListener('wheel', (e) => { |
| if (e.ctrlKey || e.metaKey) { |
| e.preventDefault(); |
| const delta = e.deltaY > 0 ? 1 / scaleStep : scaleStep; |
| updateScale(scale * delta); |
| } |
| }); |
| }); |
| } |
|
|
| |
| |
| |
| initCollapsibleSections() { |
| const collapsibles = document.querySelectorAll('.collapsible-text-container'); |
| collapsibles.forEach(container => { |
| const toggle = container.querySelector('.collapsible-toggle'); |
| const content = container.querySelector('.collapse'); |
|
|
| if (!toggle || !content) return; |
|
|
| |
| const displayField = container.closest('.display-field'); |
| const fieldKey = displayField ? displayField.dataset.fieldKey : content.id; |
| const storageKey = `potato_collapse_${fieldKey}`; |
|
|
| |
| const savedState = localStorage.getItem(storageKey); |
| if (savedState !== null) { |
| const shouldBeExpanded = savedState === 'expanded'; |
| const isCurrentlyExpanded = content.classList.contains('show'); |
|
|
| if (shouldBeExpanded !== isCurrentlyExpanded) { |
| |
| if (shouldBeExpanded) { |
| content.classList.add('show'); |
| toggle.setAttribute('aria-expanded', 'true'); |
| } else { |
| content.classList.remove('show'); |
| toggle.setAttribute('aria-expanded', 'false'); |
| } |
| } |
| } |
|
|
| |
| content.addEventListener('shown.bs.collapse', () => { |
| toggle.setAttribute('aria-expanded', 'true'); |
| localStorage.setItem(storageKey, 'expanded'); |
| }); |
|
|
| content.addEventListener('hidden.bs.collapse', () => { |
| toggle.setAttribute('aria-expanded', 'false'); |
| localStorage.setItem(storageKey, 'collapsed'); |
| }); |
| }); |
| } |
|
|
| |
| |
| |
| initSpanTargets() { |
| |
| |
|
|
| this.spanTargets.forEach(key => { |
| const field = this.displayFields[key]; |
| if (!field) return; |
|
|
| const textContent = field.element.querySelector('.text-content'); |
| if (textContent) { |
| |
| |
| if (!textContent.id) { |
| textContent.id = `text-content-${key}`; |
| } |
| } |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| initPerTurnRatings() { |
| const containers = document.querySelectorAll('.has-per-turn-ratings'); |
| containers.forEach(container => { |
| const fieldKey = container.dataset.fieldKey; |
|
|
| |
| const hiddenInputs = {}; |
| container.querySelectorAll('.per-turn-hidden').forEach(input => { |
| const schemaName = input.dataset.schemaName; |
| if (schemaName) { |
| hiddenInputs[schemaName] = input; |
| } |
| }); |
|
|
| |
| const singleHiddenInput = container.querySelector('.annotation-data-input:not(.per-turn-hidden)'); |
|
|
| |
| const ratingValuesBySchema = {}; |
|
|
| |
| container.querySelectorAll('.ptr-value').forEach(el => { |
| el.addEventListener('click', (e) => { |
| e.preventDefault(); |
| const turn = el.dataset.turn; |
| const value = parseInt(el.dataset.value, 10); |
| const schema = el.dataset.schema || ''; |
|
|
| |
| if (!ratingValuesBySchema[schema]) { |
| ratingValuesBySchema[schema] = {}; |
| } |
| const schemaValues = ratingValuesBySchema[schema]; |
|
|
| |
| if (schemaValues[turn] === value) { |
| delete schemaValues[turn]; |
| } else { |
| schemaValues[turn] = value; |
| } |
|
|
| |
| const selector = `.ptr-value[data-turn="${turn}"][data-schema="${schema}"]`; |
| container.querySelectorAll(selector).forEach(v => { |
| const vVal = parseInt(v.dataset.value, 10); |
| if (schemaValues[turn] && vVal <= schemaValues[turn]) { |
| v.classList.add('ptr-selected'); |
| } else { |
| v.classList.remove('ptr-selected'); |
| } |
| }); |
|
|
| |
| if (schema && hiddenInputs[schema]) { |
| hiddenInputs[schema].value = JSON.stringify(schemaValues); |
| } else if (singleHiddenInput) { |
| singleHiddenInput.value = JSON.stringify(schemaValues); |
| } |
|
|
| console.log('[InstanceDisplay] Per-turn rating:', fieldKey, |
| schema ? `schema=${schema}` : '', 'turn', turn, '=', |
| schemaValues[turn] || 'cleared'); |
| }); |
| }); |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| getField(key) { |
| return this.displayFields[key] || null; |
| } |
|
|
| |
| |
| |
| |
| |
| getSourceUrl(key) { |
| const field = this.getField(key); |
| if (!field) return null; |
|
|
| const sourceElement = field.element.querySelector('[data-source-url]'); |
| return sourceElement ? sourceElement.dataset.sourceUrl : null; |
| } |
|
|
| |
| |
| |
| |
| getSpanTargets() { |
| return [...this.spanTargets]; |
| } |
|
|
| |
| |
| |
| |
| isMultiSpanMode() { |
| return this.spanTargets.length > 1; |
| } |
|
|
| |
| |
| |
| |
| |
| getPrimaryTextElement() { |
| |
| if (this.spanTargets.length > 0) { |
| const key = this.spanTargets[0]; |
| const field = this.displayFields[key]; |
| if (field) { |
| return field.element.querySelector('.text-content'); |
| } |
| } |
|
|
| |
| return document.getElementById('text-content'); |
| } |
| } |
|
|
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', () => { |
| window.instanceDisplayManager = new InstanceDisplayManager(); |
| }); |
| } else { |
| window.instanceDisplayManager = new InstanceDisplayManager(); |
| } |
|
|
| |
| if (typeof module !== 'undefined' && module.exports) { |
| module.exports = InstanceDisplayManager; |
| } |
|
|
| })(); |
|
|