//Initialize globals require('./glmv-mvconfig.js') require('./glmv-hs.js') /** * Use the OOui js library to create wikis-style menu * options on the preview edits page for interaction * with the model */ buildPreviewMenu = function() { let [_, origMetadata] = extractMvconfig() //Annotation Edit Controls const addHS = new OO.ui.ButtonWidget({ icon: 'mapPinAdd', label: 'Add annotation', invisibleLabel: true, class: 'edit-button' }) addHS.on('click', readyAddHotspot) addHS.setDisabled(true) const deleteHS = new OO.ui.ButtonWidget({ icon: 'cancel', label: 'Delete annotation', invisibleLabel: true }) deleteHS.on('click', readyDelHotspot) deleteHS.setDisabled(true) const setOptions = ['default', ...Object.keys(origMetadata.annotationSets), 'Add new'] let setOptionItems = [] setOptions.forEach(opt => { setOptionItems.push(new OO.ui.MenuOptionWidget({data: opt, label: opt})) }) const setSelectHS = new OO.ui.ButtonMenuSelectWidget({ icon: 'mapTrail', label: 'Select annotation set', invisibleLabel: true, menu: { items: setOptionItems, width: 'min-content' }, $overlay: $('#bodyContent') }) setSelectHS.getMenu().on( 'choose', selSet => { onSetMenu(selSet.data) }) setSelectHS.setDisabled(true) const hotspotButtons = new OO.ui.ButtonGroupWidget({ items: [ addHS, deleteHS, setSelectHS ] }) //View Edit Controls const setView = new OO.ui.ButtonWidget({ icon: 'camera', label: 'Set Initial View', invisibleLabel: true }) setView.on('click', writeCameraOrbit) setView.setDisabled(true) const setControl = new OO.ui.ButtonWidget({ icon: 'hand', label: 'Toggle camera control', invisibleLabel: true }) setControl.on('click', () => $('model-viewer')[0].toggleAttribute('camera-controls', toggleCameraControl())) setControl.setDisabled(true) //View Limit Controls const setMinYaw = new OO.ui.ButtonWidget({ label: 'Min' }) setMinYaw.on('click', () => { writeCameraLimit('yaw','min') }) const setMaxYaw = new OO.ui.ButtonWidget({ label: 'Max' }) setMaxYaw.on('click', () => { writeCameraLimit('yaw','max') }) const yawLimitButtons = new OO.ui.ButtonGroupWidget({ items: [ setMinYaw, setMaxYaw ] }) const labelYaw = new OO.ui.LabelWidget({ label: "Yaw:" }) const yawButtons = new OO.ui.HorizontalLayout({ items: [ labelYaw, yawLimitButtons ], id: 'yaw-limits' }) const setMinPitch = new OO.ui.ButtonWidget({ label: 'Min' }) setMinPitch.on('click', () => { writeCameraLimit('pitch','min') }) const setMaxPitch = new OO.ui.ButtonWidget({ label: 'Max' }) setMaxPitch.on('click', () => { writeCameraLimit('pitch','max') }) const pitchLimitButtons = new OO.ui.ButtonGroupWidget({ items: [ setMinPitch, setMaxPitch ] }) const labelPitch = new OO.ui.LabelWidget({ label: "Pitch:" }) const pitchButtons = new OO.ui.HorizontalLayout({ items: [ labelPitch, pitchLimitButtons ], id: 'pitch-limits' }) const setLims = new OO.ui.PopupButtonWidget({ label: 'Set View Limits', invisibleLabel: true, icon: 'tableMergeCells', popup: { $content: yawButtons.$element.add(pitchButtons.$element), padded: true, position: 'above' }, $overlay: $('#bodyContent') }) setLims.setDisabled(true) const setViewConfig = [...Object.keys(origMetadata.viewerConfig), 'Add new'] let setViewItems = [] setViewConfig.forEach(opt => { setViewItems.push(new OO.ui.MenuOptionWidget({data: opt, label: opt})) }) const selectVC = new OO.ui.ButtonMenuSelectWidget({ icon: 'eye', label: 'Select view configuration', invisibleLabel: true, menu: { items: setViewItems, width: 'min-content' }, $overlay: $('#bodyContent') }) selectVC.getMenu().on( 'choose', selSet => { onViewMenu(selSet.data) }) selectVC.setDisabled(true) const cameraButtons = new OO.ui.ButtonGroupWidget({ items: [ setControl, setView, setLims, selectVC ] }) //General controls const downloadViewerImage = new OO.ui.ButtonWidget({ icon: 'imageAdd', label: 'Download current image', invisibleLabel: true }) downloadViewerImage.on('click', () => { downloadImage(mw.config.values.wgTitle) }) downloadViewerImage.setDisabled(true) const updateViewer = new OO.ui.ButtonWidget({ icon: 'reload', label: 'Update from text', invisibleLabel: true }) updateViewer.on('click', refreshConfigs) updateViewer.setDisabled(true) const generalButtons = new OO.ui.ButtonGroupWidget({ items: [ downloadViewerImage, updateViewer ] }) //Main Menu const modelMenu = new OO.ui.HorizontalLayout({ items: [ hotspotButtons, cameraButtons, generalButtons ], id: 'edit-model-menu' }) $('#wikiPreview').after(modelMenu.$element) return [modelMenu, selectVC, setSelectHS] } /** * Enable all the preview menu widgets (called by * model load event) */ enableMenu = function() { modelMenu.items.forEach(group => { group.items.forEach(el => el.setDisabled(false)) }); } /** * Disable general interaction with model * viewer for specific additional function * * @param {string} fnClass class to add to model-viewer * @param {callback} viewCall callback function to add to model-viewer * @return {Element} model-viewer element */ disableViewer = function(fnClass, viewCall) { const previewMv = $('model-viewer') if (viewCall) previewMv.one('click', viewCall) if (fnClass) previewMv.addClass(fnClass) previewMv[0].disableTap = true previewMv[0].toggleAttribute('camera-controls', false) return previewMv[0] } /** * Enable general interaction with model * viewer * * @return {Element} model-viewer element */ enableViewer = function() { const previewMv = $('model-viewer') previewMv.off('click', clickAddHotspot) previewMv.off('click', cancelDeleteHotspot) previewMv.removeClass('AddingHotspot DeletingHotspot') previewMv[0].disableTap = false previewMv[0].toggleAttribute('camera-controls', true) return previewMv[0] } /** * Use the model viewer methods to get image * of current view and download * * @param {string} defName wiki page name to use as base file name */ downloadImage = function(defName) { const imgName = defName.split('.')[0] const mView = $('model-viewer')[0] const dlA = document.createElement('a') dlA.setAttribute('download',imgName + '.png') const reader = new FileReader() reader.addEventListener("load", () => { dlA.setAttribute('href',reader.result) dlA.click() },{once: true}) mView.toBlob(null, null, true) .then(imgBlob => { reader.readAsDataURL(imgBlob) }) } /** * Process view selection menu select event * * @param {string} selectData data associated with the selected menu label */ onViewMenu = function(selectData) { if (selectData == 'Add new') { const newSelectIdx = viewSelector.menu.items.length const newView = `View${newSelectIdx}` viewSelector.menu.addItems([new OO.ui.MenuOptionWidget({data: newView, label: newView})], newSelectIdx - 1) addViewConfig(newView) } else { selectViewConfig(selectData) } } /** * Process annotation set menu select event * * @param {string} selectData data associated with the selected menu label */ onSetMenu = function(selectData) { if (selectData == 'Add new') { const newSelectIdx = setSelector.menu.items.length const newSet = `Set${newSelectIdx - 1}` setSelector.menu.addItems([new OO.ui.MenuOptionWidget({data: newSet, label: newSet})], newSelectIdx - 1) addAnnotationSet(newSet) } else { selectAnnotationSet(selectData) } } /** * Update the available menu options in the given menu * from an array * * @param {string} menuTYpe 'set'|'view' to determine which menu to update * @param {array} newOpts array of strings containing new menu options */ updateMenu = function(menuType, newOpts) { let menuObj switch (menuType) { case 'set': menuObj = setSelector menuOpts = ['default', ...newOpts, 'Add new'] break case 'view': menuObj = viewSelector menuOpts = [...newOpts, 'Add new'] break } menuObj.menu.clearItems() menuOpts.forEach(opt => { menuObj.menu.addItems([new OO.ui.MenuOptionWidget({data: opt, label: opt})]) }) } /** * Refresh all viewer settings and menus from the edit input text */ refreshConfigs = function() { const newLists = readMvconfig() if (newLists) { for (let lst in newLists) { updateMenu(lst, newLists[lst]) } } } //Initialize the menu and get required global objects const [modelMenu, viewSelector, setSelector] = buildPreviewMenu()