1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333 |
- /**
- * BCFPanel.js
- *
- * @author realor
- */
- import { Panel } from './Panel.js'
- import { Controls } from './Controls.js'
- import { ServiceDialog } from './ServiceDialog.js'
- import { MessageDialog } from './MessageDialog.js'
- import { ConfirmDialog } from './ConfirmDialog.js'
- import { LoginDialog } from './LoginDialog.js'
- import { Dialog } from './Dialog.js'
- import { TabbedPane } from './TabbedPane.js'
- import { Toast } from './Toast.js'
- import { ServiceManager } from '../io/ServiceManager.js'
- import { BCFService } from '../io/BCFService.js'
- import * as THREE from '../lib/three.module.js'
- class BCFPanel extends Panel {
- constructor(application) {
- super(application)
- this.id = 'bcf_panel'
- this.title = 'BCF'
- this.position = 'left'
- this.group = 'bcf' // service group
- this.minimumHeight = 200
- this.service = null
- this.topics = null
- this.extensions = null
- this.index = -1
- this.viewpointGuid = null
- this.viewpoints = null
- this.commentGuid = null
- this.comments = null
- this.docRefs = null
- this.docRefGuid = null
- // search panel
- this.searchPanelElem = document.createElement('div')
- this.searchPanelElem.id = 'bcf_search_panel'
- this.searchPanelElem.className = 'bcf_panel'
- this.bodyElem.appendChild(this.searchPanelElem)
- // connect panel
- this.connPanelElem = document.createElement('div')
- this.connPanelElem.className = 'bcf_body'
- this.searchPanelElem.appendChild(this.connPanelElem)
- this.bcfServiceElem = Controls.addSelectField(this.connPanelElem, 'bcfService', 'bim|label.bcf_service', [])
- this.bcfServiceElem.addEventListener('change', event => {
- let name = this.bcfServiceElem.value
- this.service = this.application.services[this.group][name]
- this.filterPanelElem.style.display = 'none'
- this.topicTableElem.style.display = 'none'
- })
- this.connButtonsElem = document.createElement('div')
- this.connPanelElem.appendChild(this.connButtonsElem)
- this.connButtonsElem.className = 'bcf_buttons'
- this.connectButton = Controls.addButton(this.connButtonsElem, 'bcfConnect', 'button.connect', () => this.refreshProjects())
- this.addServiceButton = Controls.addButton(this.connButtonsElem, 'bcfAdd', 'button.add', () => this.showAddDialog())
- this.editServiceButton = Controls.addButton(this.connButtonsElem, 'bcfEdit', 'button.edit', () => this.showEditDialog())
- this.deleteServiceButton = Controls.addButton(this.connButtonsElem, 'bcfDelete', 'button.delete', () => this.showDeleteDialog())
- // filter panel
- this.filterPanelElem = document.createElement('div')
- this.filterPanelElem.className = 'bcf_body'
- this.filterPanelElem.style.display = 'none'
- this.searchPanelElem.appendChild(this.filterPanelElem)
- this.projectElem = Controls.addSelectField(this.filterPanelElem, 'bcfProject', 'bim|label.project')
- this.projectElem.addEventListener('change', () => this.changeProject())
- this.statusFilterElem = Controls.addSelectField(this.filterPanelElem, 'bcfStatusFilter', 'bim|label.status')
- this.priorityFilterElem = Controls.addSelectField(this.filterPanelElem, 'bcfPriorityFilter', 'bim|label.priority')
- this.assignedToFilterElem = Controls.addSelectField(this.filterPanelElem, 'bcfAssignedToFilter', 'bim|label.assigned_to')
- this.filterButtonsElem = document.createElement('div')
- this.filterButtonsElem.className = 'bcf_buttons'
- this.filterPanelElem.appendChild(this.filterButtonsElem)
- this.searchTopicsButton = Controls.addButton(this.filterButtonsElem, 'searchTopics', 'button.search', () => this.searchTopics())
- this.searchTopicsButton.disabled = true
- this.setupProjectButton = Controls.addButton(this.filterButtonsElem, 'setupProject', 'button.setup', () => this.showProjectSetup())
- this.setupProjectButton.disabled = true
- this.searchNewTopicButton = Controls.addButton(this.filterButtonsElem, 'searchNewTopic', 'button.create', () => this.showTopic())
- this.searchNewTopicButton.disabled = true
- // topic table
- this.topicTableElem = Controls.addTable(this.searchPanelElem, 'topicTable', ['bim|col.index', 'bim|col.topic', 'bim|col.status'], 'data')
- this.topicTableElem.style.display = 'none'
- this.searchPanelElem.appendChild(this.topicTableElem)
- // detail panel
- this.detailPanelElem = document.createElement('div')
- this.detailPanelElem.id = 'bcf_detail_panel'
- this.detailPanelElem.className = 'bcf_panel'
- this.detailPanelElem.style.display = 'none'
- this.bodyElem.appendChild(this.detailPanelElem)
- this.detailBodyElem = document.createElement('div')
- this.detailBodyElem.className = 'bcf_body'
- this.detailPanelElem.appendChild(this.detailBodyElem)
- this.detailHeaderElem = document.createElement('div')
- this.detailHeaderElem.className = 'bcf_topic_nav'
- this.detailBodyElem.appendChild(this.detailHeaderElem)
- this.backButton = Controls.addButton(this.detailHeaderElem, 'backTopics', 'button.back', () => this.showTopicList())
- this.topicNavElem = document.createElement('span')
- this.detailHeaderElem.appendChild(this.topicNavElem)
- this.previousTopicButton = Controls.addButton(this.topicNavElem, 'previousTopic', '<', () => this.showPreviousTopic())
- this.topicSearchIndexElem = document.createElement('span')
- this.topicNavElem.appendChild(this.topicSearchIndexElem)
- this.nextTopicButton = Controls.addButton(this.topicNavElem, 'nextTopic', '>', () => this.showNextTopic())
- this.detailNewTopicButton = Controls.addButton(this.detailHeaderElem, 'detailNewTopic', 'button.create', () => this.showTopic())
- this.topicIndexElem = Controls.addTextField(this.detailBodyElem, 'topic_index', 'bim|label.index')
- this.topicIndexElem.setAttribute('readonly', true)
- this.titleElem = Controls.addTextField(this.detailBodyElem, 'topic_title', 'bim|label.title')
- this.topicTypeElem = Controls.addSelectField(this.detailBodyElem, 'topic_type', 'bim|label.type')
- this.priorityElem = Controls.addSelectField(this.detailBodyElem, 'topic_priority', 'bim|label.priority')
- this.topicStatusElem = Controls.addSelectField(this.detailBodyElem, 'topic_status', 'bim|label.status')
- this.stageElem = Controls.addSelectField(this.detailBodyElem, 'topic_stage', 'bim|label.stage')
- this.createdByElem = Controls.addTextField(this.detailBodyElem, 'topic_created_by', 'bim|label.creation_author')
- this.createdByElem.setAttribute('readonly', true)
- this.assignedToElem = Controls.addSelectField(this.detailBodyElem, 'topic_assigned_to', 'bim|label.assigned_to')
- this.dueDateElem = Controls.addDateField(this.detailBodyElem, 'due_date', 'bim|label.due_date')
- this.descriptionElem = Controls.addTextAreaField(this.detailBodyElem, 'description', 'bim|label.description', null, 'bcf_description')
- this.detailButtonsElem = document.createElement('div')
- this.detailButtonsElem.className = 'bcf_buttons'
- this.detailBodyElem.appendChild(this.detailButtonsElem)
- this.saveTopicButton = Controls.addButton(this.detailButtonsElem, 'saveTopic', 'button.save', () => this.saveTopic())
- this.deleteTopicButton = Controls.addButton(this.detailButtonsElem, 'deleteTopic', 'button.delete', () => {
- ConfirmDialog.create('bim|title.delete_topic', 'bim|question.delete_topic')
- .setAction(() => this.deleteTopic())
- .setAcceptLabel('button.delete')
- .setI18N(application.i18n)
- .show()
- })
- this.tabbedPane = new TabbedPane(this.detailPanelElem)
- this.tabbedPane.paneElem.classList.add('bcf_tabs')
- /* viewpoints panel */
- this.viewpointsPanelElem = this.tabbedPane.addTab('viewpoints', 'bim|tab.viewpoints')
- this.viewpointListElem = document.createElement('ul')
- this.viewpointListElem.classList = 'bcf_list'
- this.viewpointsPanelElem.appendChild(this.viewpointListElem)
- this.createViewpointButton = Controls.addButton(this.viewpointsPanelElem, 'createViewpoint', 'bim|button.screenshot', () => this.createViewpoint())
- this.createViewpointFromFileButton = Controls.addButton(this.viewpointsPanelElem, 'createViewpointFF', 'bim|button.upload_image', () => this.createViewpointFromFile())
- /* comments panel */
- this.commentsPanelElem = this.tabbedPane.addTab('comments', 'bim|tab.comments')
- this.commentListElem = document.createElement('ul')
- this.commentListElem.classList = 'bcf_list'
- this.commentsPanelElem.appendChild(this.commentListElem)
- this.commentElem = Controls.addTextAreaField(this.commentsPanelElem, 'comment', 'bim|label.comment')
- this.saveCommentButton = Controls.addButton(this.commentsPanelElem, 'saveComment', 'button.save', () => this.saveComment())
- this.cancelCommentButton = Controls.addButton(this.commentsPanelElem, 'cancelComment', 'button.cancel', () => this.cancelComment())
- /* document reference panel */
- this.docRefsPanelElem = this.tabbedPane.addTab('documents', 'bim|tab.doc_refs')
- this.docRefListElem = document.createElement('ul')
- this.docRefListElem.classList = 'bcf_list'
- this.docRefsPanelElem.appendChild(this.docRefListElem)
- this.docRefUrlElem = Controls.addTextAreaField(this.docRefsPanelElem, 'docRefUrl', 'bim|label.doc_ref_url')
- this.docRefDescElem = Controls.addTextAreaField(this.docRefsPanelElem, 'docRefDesc', 'bim|label.doc_ref_description')
- this.saveDocRefButton = Controls.addButton(this.docRefsPanelElem, 'saveDocRef', 'button.save', () => this.saveDocumentReference())
- this.cancelDocRefButton = Controls.addButton(this.docRefsPanelElem, 'cancelDocRef', 'button.cancel', () => this.cancelDocumentReference())
- /* audit panel */
- this.auditPanelElem = this.tabbedPane.addTab('audit', 'bim|tab.audit')
- this.auditPanelElem.classList.add('bcf_body')
- this.guidElem = Controls.addTextField(this.auditPanelElem, 'topic_guid', 'GUID:')
- this.guidElem.setAttribute('readonly', true)
- this.creationDateElem = Controls.addTextField(this.auditPanelElem, 'topic_creation_date', 'bim|label.creation_date')
- this.creationDateElem.setAttribute('readonly', true)
- this.creationAuthorElem = Controls.addTextField(this.auditPanelElem, 'topic_creation_author', 'bim|label.creation_author')
- this.creationAuthorElem.setAttribute('readonly', true)
- this.modifyDateElem = Controls.addTextField(this.auditPanelElem, 'topic_modify_date', 'bim|label.modify_date')
- this.modifyDateElem.setAttribute('readonly', true)
- this.modifyAuthorElem = Controls.addTextField(this.auditPanelElem, 'topic_modify_author', 'bim|label.modify_author')
- this.modifyAuthorElem.setAttribute('readonly', true)
- // setup panel
- this.setupPanelElem = document.createElement('div')
- this.setupPanelElem.id = 'bcf_config_panel'
- this.setupPanelElem.className = 'bcf_panel'
- this.setupPanelElem.style.display = 'none'
- this.bodyElem.appendChild(this.setupPanelElem)
- this.setupBodyElem = document.createElement('div')
- this.setupBodyElem.className = 'bcf_project_setup'
- this.setupPanelElem.appendChild(this.setupBodyElem)
- this.backSetupButton = Controls.addButton(this.setupBodyElem, 'backSetup', 'button.back', () => this.showTopicList())
- this.projectNameElem = Controls.addTextField(this.setupBodyElem, 'project_name', 'bim|label.project_name')
- this.saveProjectButtonsElem = document.createElement('div')
- this.saveProjectButtonsElem.className = 'bcf_buttons'
- this.setupBodyElem.appendChild(this.saveProjectButtonsElem)
- this.saveProjectNameButton = Controls.addButton(this.saveProjectButtonsElem, 'saveProjectName', 'button.save', () => this.saveProjectName())
- this.extensionsView = Controls.addCodeEditor(this.setupBodyElem, 'extensions_json', 'bim|label.project_extensions', '', { language: 'json', height: '200px' })
- this.saveExtensionsButtonsElem = document.createElement('div')
- this.saveExtensionsButtonsElem.className = 'bcf_buttons'
- this.setupBodyElem.appendChild(this.saveExtensionsButtonsElem)
- this.saveExtensionsButton = Controls.addButton(this.saveExtensionsButtonsElem, 'saveExtensions', 'button.save', () => this.saveProjectExtensions())
- }
- clearTopics() {
- this.topicTableElem.tBodies[0].innerHTML = ''
- }
- showTopicList() {
- this.searchPanelElem.style.display = ''
- this.detailPanelElem.style.display = 'none'
- this.setupPanelElem.style.display = 'none'
- if (this.topics === null) {
- this.searchTopics()
- }
- }
- showTopic(topic = null, index = -1) {
- // index == -1 when topic was created or udpated
- this.searchPanelElem.style.display = 'none'
- this.detailPanelElem.style.display = ''
- this.setupPanelElem.style.display = 'none'
- this.tabbedPane.paneElem.style.display = 'none'
- if (topic === null || index !== -1) {
- this.viewpointListElem.innerHTML = ''
- this.commentListElem.innerHTML = ''
- this.docRefListElem.innerHTML = ''
- }
- this.deleteTopicButton.disabled = topic === null
- this.index = index
- if (this.topics && index !== -1) {
- this.topicSearchIndexElem.innerHTML = index + 1 + ' / ' + this.topics.length
- this.previousTopicButton.disabled = index === 0
- this.nextTopicButton.disabled = index === this.topics.length - 1
- } else {
- this.topicSearchIndexElem.innerHTML = '?'
- this.previousTopicButton.disabled = true
- this.nextTopicButton.disabled = true
- }
- if (topic) {
- this.guidElem.value = topic.guid
- this.topicIndexElem.value = topic.index
- this.titleElem.value = topic.title
- this.descriptionElem.value = topic.description
- this.dueDateElem.value = this.removeTime(topic.due_date)
- this.creationDateElem.value = this.formatDate(topic.creation_date)
- this.createdByElem.value = topic.creation_author
- this.creationAuthorElem.value = topic.creation_author
- this.modifyDateElem.value = this.formatDate(topic.modify_date)
- this.modifyAuthorElem.value = topic.modify_author
- Controls.setSelectValue(this.topicTypeElem, topic.topic_type)
- Controls.setSelectValue(this.priorityElem, topic.priority)
- Controls.setSelectValue(this.topicStatusElem, topic.topic_status)
- Controls.setSelectValue(this.stageElem, topic.stage)
- Controls.setSelectValue(this.assignedToElem, topic.assigned_to)
- } else {
- this.guidElem.value = null
- this.topicIndexElem.value = null
- this.titleElem.value = null
- this.descriptionElem.value = null
- this.dueDateElem.value = null
- this.creationDateElem.value = null
- this.createdByElem.value = null
- this.creationAuthorElem.value = null
- this.modifyDateElem.value = null
- this.modifyAuthorElem.value = null
- Controls.setSelectValue(this.topicTypeElem, null)
- Controls.setSelectValue(this.priorityElem, null)
- Controls.setSelectValue(this.topicStatusElem, null)
- Controls.setSelectValue(this.stageElem, null)
- Controls.setSelectValue(this.assignedToElem, null)
- }
- if (topic) {
- this.tabbedPane.paneElem.style.display = ''
- if (index !== -1) {
- this.loadComments(false, () => this.loadViewpoints(false, () => this.loadDocumentReferences(false)))
- }
- }
- this.commentGuid = null
- this.commentElem.value = null
- this.docRefGuid = null
- this.docRefUrlElem.value = null
- this.docRefDescElem.value = null
- }
- showPreviousTopic() {
- if (this.topics) {
- let index = this.index
- if (index > 0) {
- index--
- this.showTopic(this.topics[index], index)
- }
- }
- }
- showNextTopic() {
- if (this.topics) {
- let index = this.index
- if (index >= 0 && index < this.topics.length - 1) {
- index++
- this.showTopic(this.topics[index], index)
- }
- }
- }
- searchTopics() {
- let projectId = this.getProjectId()
- if (projectId === null) return
- const onCompleted = topics => {
- this.hideProgressBar()
- this.topics = topics
- this.populateTopicTable()
- }
- const onError = error => {
- this.handleError(error, () => this.searchTopics())
- }
- let filter = {
- status: this.statusFilterElem.value,
- priority: this.priorityFilterElem.value,
- assignedTo: this.assignedToFilterElem.value
- }
- this.showProgressBar()
- this.service.getTopics(projectId, filter, onCompleted, onError)
- }
- saveTopic() {
- const application = this.application
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- let topic = {
- title: this.titleElem.value,
- topic_type: this.topicTypeElem.value,
- priority: this.priorityElem.value,
- topic_status: this.topicStatusElem.value,
- stage: this.stageElem.value,
- assigned_to: this.assignedToElem.value,
- description: this.descriptionElem.value,
- due_date: this.addTime(this.dueDateElem.value)
- }
- const onCompleted = topic => {
- this.hideProgressBar()
- this.showTopic(topic)
- this.topics = null
- Toast.create('bim|message.topic_saved')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.saveTopic())
- }
- this.showProgressBar()
- if (topicGuid) {
- // update
- this.service.updateTopic(projectId, topicGuid, topic, onCompleted, onError)
- } // creation
- else {
- this.service.createTopic(projectId, topic, onCompleted, onError)
- }
- }
- deleteTopic() {
- const application = this.application
- const onCompleted = () => {
- this.hideProgressBar()
- this.showTopic()
- this.topics = null // force topic list refresh
- Toast.create('bim|message.topic_deleted')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.deleteTopic())
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- if (topicGuid) {
- this.showProgressBar()
- this.service.deleteTopic(projectId, topicGuid, onCompleted, onError)
- }
- }
- loadViewpoints(scrollBottom, onSuccess) {
- const onCompleted = viewpoints => {
- this.hideProgressBar()
- this.viewpoints = viewpoints
- this.populateViewpointList()
- if (scrollBottom) {
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- if (onSuccess) onSuccess()
- }
- const onError = error => {
- this.handleError(error, () => this.loadViewpoints(scrollBottom, onSuccess))
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- this.showProgressBar()
- this.service.getViewpoints(projectId, topicGuid, onCompleted, onError)
- }
- createViewpoint(imageURL = null) {
- const application = this.application
- const viewpoint = {}
- const camera = application.camera
- const matrix = camera.matrixWorld
- const xAxis = new THREE.Vector3()
- const yAxis = new THREE.Vector3()
- const zAxis = new THREE.Vector3()
- const position = new THREE.Vector3()
- matrix.extractBasis(xAxis, yAxis, zAxis)
- position.setFromMatrixPosition(matrix)
- if (camera instanceof THREE.PerspectiveCamera) {
- viewpoint.perspective_camera = {
- camera_view_point: { x: position.x, y: position.y, z: position.z },
- camera_direction: { x: zAxis.x, y: zAxis.y, z: zAxis.z },
- camera_up_vector: { x: yAxis.x, y: yAxis.y, z: yAxis.z },
- field_of_view: camera.fov
- }
- } else if (camera instanceof THREE.OrthographicCamera) {
- viewpoint.orthogonal_camera = {
- camera_view_point: { x: position.x, y: position.y, z: position.z },
- camera_direction: { x: zAxis.x, y: zAxis.y, z: zAxis.z },
- camera_up_vector: { x: yAxis.x, y: yAxis.y, z: yAxis.z },
- view_to_world_scale: (0.5 * camera.zoom) / camera.right
- }
- }
- const onCompleted = viewpoint => {
- this.hideProgressBar()
- this.loadViewpoints(true)
- Toast.create('bim|message.viewpoint_saved')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.createViewpoint(imageURL))
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- if (imageURL === null) {
- // capture image from canvas
- const canvas = this.application.renderer.domElement
- imageURL = canvas.toDataURL('image/png')
- }
- let format = null
- if (imageURL.startsWith('data:image/jpeg;base64,')) {
- format = 'jpeg'
- } else if (imageURL.startsWith('data:image/png;base64,')) {
- format = 'png'
- }
- if (format) {
- const index = imageURL.indexOf(',')
- let image = imageURL.substring(index + 1)
- viewpoint.snapshot = {
- snapshot_type: format,
- snapshot_data: image
- }
- this.showProgressBar()
- this.service.createViewpoint(projectId, topicGuid, viewpoint, onCompleted, onError)
- } else {
- this.handleError({ code: 0, message: 'Unsupported image format' })
- }
- }
- createViewpointFromFile() {
- const onChange = () => {
- let files = this.inputFile.files
- if (files.length > 0) {
- let file = files[0]
- let reader = new FileReader()
- reader.onload = () => {
- let imageURL = reader.result
- this.createViewpoint(imageURL)
- }
- reader.readAsDataURL(file)
- }
- }
- let inputFile = document.createElement('input')
- this.inputFile = inputFile
- inputFile.type = 'file'
- inputFile.id = this.name + '_file'
- inputFile.accept = '.jpeg, .jpg, .png'
- inputFile.addEventListener('change', onChange, false)
- inputFile.click()
- }
- deleteViewpoint(viewpoint) {
- const application = this.application
- const onCompleted = () => {
- this.hideProgressBar()
- this.loadViewpoints()
- Toast.create('bim|message.viewpoint_deleted')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.deleteViewpoint(viewpoint))
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- if (topicGuid) {
- this.showProgressBar()
- this.service.deleteViewpoint(projectId, topicGuid, viewpoint.guid, onCompleted, onError)
- }
- }
- loadComments(scrollBottom, onSuccess) {
- const onCompleted = comments => {
- this.hideProgressBar()
- this.comments = comments
- this.populateCommentList()
- if (scrollBottom) {
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- if (onSuccess) onSuccess()
- }
- const onError = error => {
- this.handleError(error, () => this.loadComments(scrollBottom, onSuccess))
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- this.showProgressBar()
- this.service.getComments(projectId, topicGuid, onCompleted, onError)
- }
- saveComment() {
- const application = this.application
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- let comment = {
- comment: this.commentElem.value
- }
- const onCompleted = comment => {
- this.hideProgressBar()
- this.commentElem.value = null
- this.commentGuid = null
- this.loadComments(true)
- Toast.create('bim|message.comment_saved')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.saveComment())
- }
- this.showProgressBar()
- if (this.commentGuid) {
- // update
- this.service.updateComment(projectId, topicGuid, this.commentGuid, comment, onCompleted, onError)
- } // creation
- else {
- this.service.createComment(projectId, topicGuid, comment, onCompleted, onError)
- }
- }
- deleteComment(comment) {
- const application = this.application
- const onCompleted = () => {
- this.hideProgressBar()
- this.loadComments()
- Toast.create('bim|message.comment_deleted')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.deleteComment())
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- if (topicGuid) {
- this.showProgressBar()
- this.service.deleteComment(projectId, topicGuid, comment.guid, onCompleted, onError)
- }
- }
- editComment(comment) {
- this.commentGuid = comment.guid
- this.commentElem.value = comment.comment
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- cancelComment() {
- this.commentGuid = null
- this.commentElem.value = null
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- loadDocumentReferences(scrollBottom, onSuccess) {
- const onCompleted = docRefs => {
- this.hideProgressBar()
- this.docRefs = docRefs
- this.populateDocumentReferenceList()
- if (scrollBottom) {
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- if (onSuccess) onSuccess()
- }
- const onError = error => {
- this.handleError(error, () => this.loadDocumentReferences(scrollBottom, onSuccess))
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- this.showProgressBar()
- this.service.getDocumentReferences(projectId, topicGuid, onCompleted, onError)
- }
- saveDocumentReference() {
- const application = this.application
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- let docRef = {
- url: this.docRefUrlElem.value,
- description: this.docRefDescElem.value
- }
- const onCompleted = docRef => {
- this.hideProgressBar()
- this.docRefUrlElem.value = null
- this.docRefDescElem.value = null
- this.docRefGuid = null
- this.loadDocumentReferences(true)
- Toast.create('bim|message.doc_ref_saved')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.saveDocumentReference())
- }
- this.showProgressBar()
- if (this.docRefGuid) {
- // update
- this.service.updateDocumentReference(projectId, topicGuid, this.docRefGuid, docRef, onCompleted, onError)
- } // creation
- else {
- this.service.createDocumentReference(projectId, topicGuid, docRef, onCompleted, onError)
- }
- }
- editDocumentReference(docRef) {
- this.docRefGuid = docRef.guid
- this.docRefUrlElem.value = docRef.url
- this.docRefDescElem.value = docRef.description
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- cancelDocumentReference() {
- this.docRefGuid = null
- this.docRefUrlElem.value = null
- this.docRefDescElem.value = null
- this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
- }
- deleteDocumentReference(docRef) {
- const application = this.application
- const onCompleted = () => {
- this.hideProgressBar()
- this.loadDocumentReferences()
- Toast.create('bim|message.doc_ref_deleted')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.deleteDocumentReference(docRef))
- }
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- if (topicGuid) {
- this.showProgressBar()
- this.service.deleteDocumentReference(projectId, topicGuid, docRef.guid, onCompleted, onError)
- }
- }
- updateExtensions() {
- let projectId = this.getProjectId()
- if (projectId === null) return
- const onCompleted = extensions => {
- this.hideProgressBar()
- this.extensions = extensions
- this.populateExtensions()
- }
- const onError = error => {
- this.handleError(error, () => this.updateExtensions())
- }
- this.showProgressBar()
- this.service.getExtensions(projectId, onCompleted, onError)
- }
- refreshProjects() {
- const projects = []
- const onCompleted = serverProjects => {
- this.hideProgressBar()
- this.filterPanelElem.style.display = ''
- this.topicTableElem.style.display = ''
- const projectIdSet = new Set()
- for (let serverProject of serverProjects) {
- let projectId = serverProject.project_id
- let projectName = serverProject.name
- projectIdSet.add(projectId)
- projects.push([projectId, projectName])
- }
- const scene = this.application.scene
- scene.traverse(object => {
- if (object._ifc && object._ifc.constructor.name === 'IfcProject') {
- let projectId = object._ifc.GlobalId
- let projectName = object._ifc.Name || object._ifc.LongName
- if (!projectIdSet.has(projectId)) {
- projects.push([projectId, projectName])
- }
- }
- })
- Controls.setSelectOptions(this.projectElem, projects)
- const disabled = projects.length === 0
- this.searchTopicsButton.disabled = disabled
- this.setupProjectButton.disabled = disabled
- this.searchNewTopicButton.disabled = disabled
- this.updateExtensions()
- }
- const onError = error => {
- this.handleError(error, () => this.refreshProjects())
- }
- this.clearTopics()
- this.showProgressBar()
- this.service.getProjects(onCompleted, onError)
- }
- saveProjectName() {
- const application = this.application
- const projectName = this.projectNameElem.value
- const index = this.projectElem.selectedIndex
- const options = this.projectElem.options
- const oldProjectName = options[index].label
- if (projectName !== oldProjectName) {
- const onCompleted = project => {
- this.hideProgressBar()
- options[index].label = project.name
- Toast.create('bim|message.project_saved')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.saveProjectName())
- }
- const projectId = this.getProjectId()
- const project = {
- name: projectName
- }
- this.showProgressBar()
- this.service.updateProject(projectId, project, onCompleted, onError)
- }
- }
- saveProjectExtensions() {
- const application = this.application
- const extensionsText = this.extensionsView.state.doc.toString()
- const oldExtensionsText = JSON.stringify(this.extensions, null, 2)
- if (extensionsText !== oldExtensionsText) {
- const onCompleted = extensions => {
- this.hideProgressBar()
- this.extensions = extensions
- this.populateExtensions()
- Toast.create('bim|message.project_extensions_saved')
- .setI18N(application.i18n)
- .show()
- }
- const onError = error => {
- this.handleError(error, () => this.saveProjectExtensions())
- }
- try {
- let extensions = JSON.parse(extensionsText)
- const projectId = this.getProjectId()
- this.showProgressBar()
- this.service.updateExtensions(projectId, extensions, onCompleted, onError)
- } catch (ex) {
- console.error(ex)
- }
- }
- }
- showViewpoint(viewpoint) {
- const application = this.application
- let position, dir, up, camera
- if (viewpoint.perspective_camera) {
- const pcam = viewpoint.perspective_camera
- position = pcam.camera_view_point
- dir = pcam.camera_direction
- up = pcam.camera_up_vector
- let fov = pcam.field_of_view
- camera = this.application.perspectiveCamera
- camera.fov = fov
- } else {
- const ocam = viewpoint.orthogonal_camera
- position = ocam.camera_view_point
- dir = ocam.camera_direction
- up = ocam.camera_up_vector
- let scale = ocam.view_to_world_scale
- camera = this.application.orthographicCamera
- camera.zoom = scale
- camera.right = 0.5
- camera.left = -0.5
- camera.top = 0.1
- camera.bottom = -0.1
- camera.near = -100
- camera.far = 100
- }
- const xAxis = new THREE.Vector3()
- const yAxis = new THREE.Vector3(up.x, up.y, up.z)
- const zAxis = new THREE.Vector3(dir.x, dir.y, dir.z)
- xAxis.crossVectors(yAxis, zAxis).normalize()
- yAxis.normalize()
- zAxis.normalize()
- camera.matrix.makeBasis(xAxis, yAxis, zAxis)
- camera.matrix.setPosition(position.x, position.y, position.z)
- camera.matrix.decompose(camera.position, camera.quaternion, camera.scale)
- camera.updateMatrixWorld(true)
- application.notifyObjectsChanged(camera, this)
- application.activateCamera(camera)
- }
- showProjectSetup() {
- this.searchPanelElem.style.display = 'none'
- this.detailPanelElem.style.display = 'none'
- this.setupPanelElem.style.display = ''
- const index = this.projectElem.selectedIndex
- const options = this.projectElem.options
- this.projectNameElem.value = options[index].label
- const json = JSON.stringify(this.extensions, null, 2)
- const state = this.extensionsView.state
- const tx = state.update({ changes: { from: 0, to: state.doc.length, insert: json } })
- this.extensionsView.dispatch(tx)
- }
- populateTopicTable() {
- const topics = this.topics
- const topicsElem = this.topicTableElem
- topicsElem.tBodies[0].innerHTML = ''
- for (let i = 0; i < topics.length; i++) {
- let topic = topics[i]
- let rowElem = Controls.addTableRow(topicsElem)
- const openTopic = () => {
- this.showTopic(topic, i)
- }
- Controls.addLink(rowElem.children[0], topic.index, '#', null, null, openTopic)
- Controls.addLink(rowElem.children[1], topic.title, '#', null, null, openTopic)
- rowElem.children[2].innerHTML = topic.topic_status
- }
- }
- populateCommentList() {
- const comments = this.comments
- const commentsElem = this.commentListElem
- commentsElem.innerHTML = ''
- for (let comment of comments) {
- let itemListElem = document.createElement('li')
- let itemListHeaderElem = document.createElement('div')
- itemListElem.appendChild(itemListHeaderElem)
- let spanElem = document.createElement('span')
- spanElem.className = 'icon comment'
- itemListHeaderElem.appendChild(spanElem)
- let authorDate = comment.author || 'anonymous'
- if (comment.date) {
- authorDate += ' (' + this.formatDate(comment.date) + ')'
- }
- authorDate += ':'
- Controls.addText(itemListHeaderElem, authorDate, 'bcf_comment_author')
- Controls.addButton(itemListHeaderElem, 'updateComment', 'button.edit', () => this.editComment(comment), 'bcf_edit_comment')
- Controls.addButton(
- itemListHeaderElem,
- 'deleteComment',
- 'button.delete',
- () => {
- ConfirmDialog.create('bim|title.delete_comment', 'bim|question.delete_comment')
- .setAction(() => this.deleteComment(comment))
- .setAcceptLabel('button.delete')
- .setI18N(this.application.i18n)
- .show()
- },
- 'bcf_delete_comment'
- )
- Controls.addText(itemListElem, comment.comment, 'bcf_comment_text')
- this.application.i18n.updateTree(itemListElem)
- commentsElem.appendChild(itemListElem)
- }
- }
- populateDocumentReferenceList() {
- const docRefs = this.docRefs
- const docRefsElem = this.docRefListElem
- docRefsElem.innerHTML = ''
- for (let docRef of docRefs) {
- let itemListElem = document.createElement('li')
- let spanElem = document.createElement('span')
- spanElem.className = 'icon doc_ref'
- itemListElem.appendChild(spanElem)
- let linkElem = document.createElement('a')
- linkElem.href = docRef.url
- linkElem.target = '_blank'
- linkElem.innerHTML = docRef.description
- itemListElem.appendChild(linkElem)
- Controls.addButton(itemListElem, 'updateDocRef', 'button.edit', () => this.editDocumentReference(docRef), 'bcf_edit_doc_ref')
- Controls.addButton(
- itemListElem,
- 'deleteDocRef',
- 'button.delete',
- () => {
- ConfirmDialog.create('bim|title.delete_doc_ref', 'bim|question.delete_doc_ref')
- .setAction(() => this.deleteDocumentReference(docRef))
- .setAcceptLabel('button.delete')
- .setI18N(this.application.i18n)
- .show()
- },
- 'bcf_delete_doc_ref'
- )
- this.application.i18n.updateTree(itemListElem)
- docRefsElem.appendChild(itemListElem)
- }
- }
- populateViewpointList() {
- let projectId = this.getProjectId()
- let topicGuid = this.guidElem.value
- const viewpoints = this.viewpoints
- const viewpointsElem = this.viewpointListElem
- viewpointsElem.innerHTML = ''
- for (let viewpoint of viewpoints) {
- let itemListElem = document.createElement('li')
- let spanElem = document.createElement('span')
- spanElem.className = 'icon viewpoint'
- itemListElem.appendChild(spanElem)
- let vpType = ''
- if (viewpoint.perspective_camera) {
- vpType = ' (P)'
- } else if (viewpoint.orthogonal_camera) {
- vpType += ' (O)'
- }
- Controls.addTextWithArgs(itemListElem, 'bim|message.viewpoint', [viewpoint.index || '', vpType], 'bcf_viewpoint_text')
- Controls.addButton(itemListElem, 'showViewpoint', 'button.view', () => this.showViewpoint(viewpoint), 'bcf_show_viewpoint')
- Controls.addButton(
- itemListElem,
- 'deleteViewpoint',
- 'button.delete',
- () => {
- ConfirmDialog.create('bim|title.delete_viewpoint', 'bim|question.delete_viewpoint')
- .setAction(() => this.deleteViewpoint(viewpoint))
- .setAcceptLabel('button.delete')
- .setI18N(this.application.i18n)
- .show()
- },
- 'bcf_delete_viewpoint'
- )
- this.application.i18n.updateTree(itemListElem)
- if (viewpoint.snapshot) {
- let type = viewpoint.snapshot.snapshot_type
- let data = viewpoint.snapshot.snapshot_data
- let source
- if (data) {
- source = type === 'png' ? 'data:image/png;base64,' + data : 'data:image/jpeg;base64,' + data
- } else {
- source = this.service.url + '/bcf/2.1/projects/' + projectId + '/topics/' + topicGuid + '/viewpoints/' + viewpoint.guid + '/snapshot'
- }
- let linkElem = Controls.addLink(itemListElem, null, '#', 'bim|label.zoom_image', 'viewpoint_snapshot', () => this.zoomSnapshot(source))
- let imageElem = document.createElement('img')
- imageElem.className = 'viewpoint_snapshot'
- imageElem.src = source
- linkElem.appendChild(imageElem)
- }
- viewpointsElem.appendChild(itemListElem)
- }
- }
- zoomSnapshot(source) {
- const dialog = new Dialog('bim|title.viewpoint')
- dialog.setI18N(this.application.i18n)
- dialog.setClassName('viewpoint_dialog')
- let imageElem = document.createElement('img')
- imageElem.className = 'snapshot_zoom'
- imageElem.src = source
- imageElem.onload = () => {
- const container = this.application.container
- let imageWidth = imageElem.width
- let imageHeight = imageElem.height
- let imageAspectRatio = imageWidth / imageHeight
- let screenAspectRatio = container.clientWidth / container.clientHeight
- if (imageAspectRatio > screenAspectRatio) {
- let width = container.clientWidth
- dialog.setSize(width, width / imageAspectRatio + 100)
- } else {
- let height = container.clientHeight
- dialog.setSize((height - 100) * imageAspectRatio, height)
- }
- let acceptButton = dialog.addButton('accept', 'button.close', () => {
- dialog.hide()
- })
- dialog.onShow = () => acceptButton.focus()
- dialog.show()
- }
- dialog.bodyElem.appendChild(imageElem)
- }
- populateExtensions() {
- const ext = this.extensions
- Controls.setSelectOptions(this.statusFilterElem, [''].concat(ext.topic_status))
- Controls.setSelectOptions(this.priorityFilterElem, [''].concat(ext.priority))
- Controls.setSelectOptions(this.assignedToFilterElem, [''].concat(ext.user_id_type))
- Controls.setSelectOptions(this.topicTypeElem, ext.topic_type)
- Controls.setSelectOptions(this.priorityElem, ext.priority)
- Controls.setSelectOptions(this.topicStatusElem, ext.topic_status)
- Controls.setSelectOptions(this.stageElem, ext.stage)
- Controls.setSelectOptions(this.assignedToElem, ext.user_id_type)
- }
- getProjectId() {
- let projectId = this.projectElem.value
- if (projectId === '') projectId = null
- return projectId
- }
- changeProject() {
- this.clearTopics()
- this.updateExtensions()
- }
- formatDate(dateString) {
- if (dateString === null || dateString === '') return null
- const index = dateString.indexOf('T')
- return index === -1 ? dateString : dateString.substring(0, index) + ' ' + dateString.substring(index + 1)
- }
- addTime(dateString) {
- if (dateString === null || dateString === '') return null
- return dateString + 'T00:00:00'
- }
- removeTime(dateString) {
- if (dateString === null || dateString === '') return null
- const index = dateString.indexOf('T')
- return dateString.substring(0, index)
- }
- onShow() {
- this.updateServices()
- }
- updateServices() {
- const application = this.application
- const services = application.services[this.group]
- let options = []
- for (let name in services) {
- let service = services[name]
- options.push([service.name, service.description || service.name])
- }
- Controls.setSelectOptions(this.bcfServiceElem, options)
- if (options.length > 0) {
- let name = this.bcfServiceElem.value
- this.service = application.services[this.group][name]
- } else {
- this.service = null
- }
- let service = this.service
- this.connectButton.style.display = service ? '' : 'none'
- this.addServiceButton.style.display = ''
- this.editServiceButton.style.display = service ? '' : 'none'
- this.deleteServiceButton.style.display = service ? '' : 'none'
- }
- showAddDialog() {
- let serviceTypes = ServiceManager.getTypesOf(BCFService)
- let dialog = new ServiceDialog('Add BCF service', serviceTypes)
- dialog.serviceTypeSelect.disabled = true
- dialog.setI18N(this.application.i18n)
- dialog.onSave = (serviceType, name, description, url, username, password) => {
- const service = new ServiceManager.classes[serviceType]()
- service.name = name
- service.description = description
- service.url = url
- service.username = username
- service.password = password
- this.application.addService(service, this.group)
- this.updateServices()
- this.service = service
- this.bcfServiceElem.value = name
- this.filterPanelElem.style.display = 'none'
- this.topicTableElem.style.display = 'none'
- }
- dialog.show()
- }
- showEditDialog() {
- if (this.service === null) return
- const service = this.service
- let serviceTypes = ServiceManager.getTypesOf(BCFService)
- let dialog = new ServiceDialog('Edit BCF service', serviceTypes, service.constructor.type, service.name, service.description, service.url, service.username, service.password)
- dialog.serviceTypeSelect.disabled = true
- dialog.setI18N(this.application.i18n)
- dialog.nameElem.readOnly = true
- dialog.onSave = (serviceType, name, description, url, username, password) => {
- service.description = description
- service.url = url
- service.username = username
- service.password = password
- this.application.addService(service, this.group)
- this.updateServices()
- this.filterPanelElem.style.display = 'none'
- this.topicTableElem.style.display = 'none'
- }
- dialog.show()
- }
- showDeleteDialog() {
- const application = this.application
- let name = this.bcfServiceElem.value
- if (name) {
- ConfirmDialog.create('bim|title.delete_bcf_service', 'bim|question.delete_bcf_service', name)
- .setAction(() => {
- let service = application.services[this.group][name]
- application.removeService(service, this.group)
- this.updateServices()
- this.filterPanelElem.style.display = 'none'
- this.topicTableElem.style.display = 'none'
- })
- .setAcceptLabel('button.delete')
- .setI18N(application.i18n)
- .show()
- }
- }
- handleError(error, onLogin) {
- this.hideProgressBar()
- if (error.code === 401) {
- this.requestCredentials('message.invalid_credentials', onLogin)
- } else if (error.code === 403) {
- this.requestCredentials('message.action_denied', onLogin)
- } else {
- let message = error.message
- MessageDialog.create('ERROR', message)
- .setClassName('error')
- .setI18N(this.application.i18n)
- .show()
- }
- }
- requestCredentials(message, onLogin, onFailed) {
- const loginDialog = new LoginDialog(this.application, message)
- loginDialog.login = (username, password) => {
- this.service.username = username
- this.service.password = password
- if (onLogin) onLogin()
- }
- loginDialog.onCancel = () => {
- loginDialog.hide()
- if (onFailed) onFailed()
- }
- loginDialog.show()
- }
- showProgressBar() {
- this.application.progressBar.message = ''
- this.application.progressBar.progress = undefined
- this.application.progressBar.visible = true
- }
- hideProgressBar() {
- this.application.progressBar.visible = false
- }
- }
- export { BCFPanel }
|