BCFPanel.js 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. /**
  2. * BCFPanel.js
  3. *
  4. * @author realor
  5. */
  6. import { Panel } from './Panel.js'
  7. import { Controls } from './Controls.js'
  8. import { ServiceDialog } from './ServiceDialog.js'
  9. import { MessageDialog } from './MessageDialog.js'
  10. import { ConfirmDialog } from './ConfirmDialog.js'
  11. import { LoginDialog } from './LoginDialog.js'
  12. import { Dialog } from './Dialog.js'
  13. import { TabbedPane } from './TabbedPane.js'
  14. import { Toast } from './Toast.js'
  15. import { ServiceManager } from '../io/ServiceManager.js'
  16. import { BCFService } from '../io/BCFService.js'
  17. import * as THREE from '../lib/three.module.js'
  18. class BCFPanel extends Panel {
  19. constructor(application) {
  20. super(application)
  21. this.id = 'bcf_panel'
  22. this.title = 'BCF'
  23. this.position = 'left'
  24. this.group = 'bcf' // service group
  25. this.minimumHeight = 200
  26. this.service = null
  27. this.topics = null
  28. this.extensions = null
  29. this.index = -1
  30. this.viewpointGuid = null
  31. this.viewpoints = null
  32. this.commentGuid = null
  33. this.comments = null
  34. this.docRefs = null
  35. this.docRefGuid = null
  36. // search panel
  37. this.searchPanelElem = document.createElement('div')
  38. this.searchPanelElem.id = 'bcf_search_panel'
  39. this.searchPanelElem.className = 'bcf_panel'
  40. this.bodyElem.appendChild(this.searchPanelElem)
  41. // connect panel
  42. this.connPanelElem = document.createElement('div')
  43. this.connPanelElem.className = 'bcf_body'
  44. this.searchPanelElem.appendChild(this.connPanelElem)
  45. this.bcfServiceElem = Controls.addSelectField(this.connPanelElem, 'bcfService', 'bim|label.bcf_service', [])
  46. this.bcfServiceElem.addEventListener('change', event => {
  47. let name = this.bcfServiceElem.value
  48. this.service = this.application.services[this.group][name]
  49. this.filterPanelElem.style.display = 'none'
  50. this.topicTableElem.style.display = 'none'
  51. })
  52. this.connButtonsElem = document.createElement('div')
  53. this.connPanelElem.appendChild(this.connButtonsElem)
  54. this.connButtonsElem.className = 'bcf_buttons'
  55. this.connectButton = Controls.addButton(this.connButtonsElem, 'bcfConnect', 'button.connect', () => this.refreshProjects())
  56. this.addServiceButton = Controls.addButton(this.connButtonsElem, 'bcfAdd', 'button.add', () => this.showAddDialog())
  57. this.editServiceButton = Controls.addButton(this.connButtonsElem, 'bcfEdit', 'button.edit', () => this.showEditDialog())
  58. this.deleteServiceButton = Controls.addButton(this.connButtonsElem, 'bcfDelete', 'button.delete', () => this.showDeleteDialog())
  59. // filter panel
  60. this.filterPanelElem = document.createElement('div')
  61. this.filterPanelElem.className = 'bcf_body'
  62. this.filterPanelElem.style.display = 'none'
  63. this.searchPanelElem.appendChild(this.filterPanelElem)
  64. this.projectElem = Controls.addSelectField(this.filterPanelElem, 'bcfProject', 'bim|label.project')
  65. this.projectElem.addEventListener('change', () => this.changeProject())
  66. this.statusFilterElem = Controls.addSelectField(this.filterPanelElem, 'bcfStatusFilter', 'bim|label.status')
  67. this.priorityFilterElem = Controls.addSelectField(this.filterPanelElem, 'bcfPriorityFilter', 'bim|label.priority')
  68. this.assignedToFilterElem = Controls.addSelectField(this.filterPanelElem, 'bcfAssignedToFilter', 'bim|label.assigned_to')
  69. this.filterButtonsElem = document.createElement('div')
  70. this.filterButtonsElem.className = 'bcf_buttons'
  71. this.filterPanelElem.appendChild(this.filterButtonsElem)
  72. this.searchTopicsButton = Controls.addButton(this.filterButtonsElem, 'searchTopics', 'button.search', () => this.searchTopics())
  73. this.searchTopicsButton.disabled = true
  74. this.setupProjectButton = Controls.addButton(this.filterButtonsElem, 'setupProject', 'button.setup', () => this.showProjectSetup())
  75. this.setupProjectButton.disabled = true
  76. this.searchNewTopicButton = Controls.addButton(this.filterButtonsElem, 'searchNewTopic', 'button.create', () => this.showTopic())
  77. this.searchNewTopicButton.disabled = true
  78. // topic table
  79. this.topicTableElem = Controls.addTable(this.searchPanelElem, 'topicTable', ['bim|col.index', 'bim|col.topic', 'bim|col.status'], 'data')
  80. this.topicTableElem.style.display = 'none'
  81. this.searchPanelElem.appendChild(this.topicTableElem)
  82. // detail panel
  83. this.detailPanelElem = document.createElement('div')
  84. this.detailPanelElem.id = 'bcf_detail_panel'
  85. this.detailPanelElem.className = 'bcf_panel'
  86. this.detailPanelElem.style.display = 'none'
  87. this.bodyElem.appendChild(this.detailPanelElem)
  88. this.detailBodyElem = document.createElement('div')
  89. this.detailBodyElem.className = 'bcf_body'
  90. this.detailPanelElem.appendChild(this.detailBodyElem)
  91. this.detailHeaderElem = document.createElement('div')
  92. this.detailHeaderElem.className = 'bcf_topic_nav'
  93. this.detailBodyElem.appendChild(this.detailHeaderElem)
  94. this.backButton = Controls.addButton(this.detailHeaderElem, 'backTopics', 'button.back', () => this.showTopicList())
  95. this.topicNavElem = document.createElement('span')
  96. this.detailHeaderElem.appendChild(this.topicNavElem)
  97. this.previousTopicButton = Controls.addButton(this.topicNavElem, 'previousTopic', '<', () => this.showPreviousTopic())
  98. this.topicSearchIndexElem = document.createElement('span')
  99. this.topicNavElem.appendChild(this.topicSearchIndexElem)
  100. this.nextTopicButton = Controls.addButton(this.topicNavElem, 'nextTopic', '>', () => this.showNextTopic())
  101. this.detailNewTopicButton = Controls.addButton(this.detailHeaderElem, 'detailNewTopic', 'button.create', () => this.showTopic())
  102. this.topicIndexElem = Controls.addTextField(this.detailBodyElem, 'topic_index', 'bim|label.index')
  103. this.topicIndexElem.setAttribute('readonly', true)
  104. this.titleElem = Controls.addTextField(this.detailBodyElem, 'topic_title', 'bim|label.title')
  105. this.topicTypeElem = Controls.addSelectField(this.detailBodyElem, 'topic_type', 'bim|label.type')
  106. this.priorityElem = Controls.addSelectField(this.detailBodyElem, 'topic_priority', 'bim|label.priority')
  107. this.topicStatusElem = Controls.addSelectField(this.detailBodyElem, 'topic_status', 'bim|label.status')
  108. this.stageElem = Controls.addSelectField(this.detailBodyElem, 'topic_stage', 'bim|label.stage')
  109. this.createdByElem = Controls.addTextField(this.detailBodyElem, 'topic_created_by', 'bim|label.creation_author')
  110. this.createdByElem.setAttribute('readonly', true)
  111. this.assignedToElem = Controls.addSelectField(this.detailBodyElem, 'topic_assigned_to', 'bim|label.assigned_to')
  112. this.dueDateElem = Controls.addDateField(this.detailBodyElem, 'due_date', 'bim|label.due_date')
  113. this.descriptionElem = Controls.addTextAreaField(this.detailBodyElem, 'description', 'bim|label.description', null, 'bcf_description')
  114. this.detailButtonsElem = document.createElement('div')
  115. this.detailButtonsElem.className = 'bcf_buttons'
  116. this.detailBodyElem.appendChild(this.detailButtonsElem)
  117. this.saveTopicButton = Controls.addButton(this.detailButtonsElem, 'saveTopic', 'button.save', () => this.saveTopic())
  118. this.deleteTopicButton = Controls.addButton(this.detailButtonsElem, 'deleteTopic', 'button.delete', () => {
  119. ConfirmDialog.create('bim|title.delete_topic', 'bim|question.delete_topic')
  120. .setAction(() => this.deleteTopic())
  121. .setAcceptLabel('button.delete')
  122. .setI18N(application.i18n)
  123. .show()
  124. })
  125. this.tabbedPane = new TabbedPane(this.detailPanelElem)
  126. this.tabbedPane.paneElem.classList.add('bcf_tabs')
  127. /* viewpoints panel */
  128. this.viewpointsPanelElem = this.tabbedPane.addTab('viewpoints', 'bim|tab.viewpoints')
  129. this.viewpointListElem = document.createElement('ul')
  130. this.viewpointListElem.classList = 'bcf_list'
  131. this.viewpointsPanelElem.appendChild(this.viewpointListElem)
  132. this.createViewpointButton = Controls.addButton(this.viewpointsPanelElem, 'createViewpoint', 'bim|button.screenshot', () => this.createViewpoint())
  133. this.createViewpointFromFileButton = Controls.addButton(this.viewpointsPanelElem, 'createViewpointFF', 'bim|button.upload_image', () => this.createViewpointFromFile())
  134. /* comments panel */
  135. this.commentsPanelElem = this.tabbedPane.addTab('comments', 'bim|tab.comments')
  136. this.commentListElem = document.createElement('ul')
  137. this.commentListElem.classList = 'bcf_list'
  138. this.commentsPanelElem.appendChild(this.commentListElem)
  139. this.commentElem = Controls.addTextAreaField(this.commentsPanelElem, 'comment', 'bim|label.comment')
  140. this.saveCommentButton = Controls.addButton(this.commentsPanelElem, 'saveComment', 'button.save', () => this.saveComment())
  141. this.cancelCommentButton = Controls.addButton(this.commentsPanelElem, 'cancelComment', 'button.cancel', () => this.cancelComment())
  142. /* document reference panel */
  143. this.docRefsPanelElem = this.tabbedPane.addTab('documents', 'bim|tab.doc_refs')
  144. this.docRefListElem = document.createElement('ul')
  145. this.docRefListElem.classList = 'bcf_list'
  146. this.docRefsPanelElem.appendChild(this.docRefListElem)
  147. this.docRefUrlElem = Controls.addTextAreaField(this.docRefsPanelElem, 'docRefUrl', 'bim|label.doc_ref_url')
  148. this.docRefDescElem = Controls.addTextAreaField(this.docRefsPanelElem, 'docRefDesc', 'bim|label.doc_ref_description')
  149. this.saveDocRefButton = Controls.addButton(this.docRefsPanelElem, 'saveDocRef', 'button.save', () => this.saveDocumentReference())
  150. this.cancelDocRefButton = Controls.addButton(this.docRefsPanelElem, 'cancelDocRef', 'button.cancel', () => this.cancelDocumentReference())
  151. /* audit panel */
  152. this.auditPanelElem = this.tabbedPane.addTab('audit', 'bim|tab.audit')
  153. this.auditPanelElem.classList.add('bcf_body')
  154. this.guidElem = Controls.addTextField(this.auditPanelElem, 'topic_guid', 'GUID:')
  155. this.guidElem.setAttribute('readonly', true)
  156. this.creationDateElem = Controls.addTextField(this.auditPanelElem, 'topic_creation_date', 'bim|label.creation_date')
  157. this.creationDateElem.setAttribute('readonly', true)
  158. this.creationAuthorElem = Controls.addTextField(this.auditPanelElem, 'topic_creation_author', 'bim|label.creation_author')
  159. this.creationAuthorElem.setAttribute('readonly', true)
  160. this.modifyDateElem = Controls.addTextField(this.auditPanelElem, 'topic_modify_date', 'bim|label.modify_date')
  161. this.modifyDateElem.setAttribute('readonly', true)
  162. this.modifyAuthorElem = Controls.addTextField(this.auditPanelElem, 'topic_modify_author', 'bim|label.modify_author')
  163. this.modifyAuthorElem.setAttribute('readonly', true)
  164. // setup panel
  165. this.setupPanelElem = document.createElement('div')
  166. this.setupPanelElem.id = 'bcf_config_panel'
  167. this.setupPanelElem.className = 'bcf_panel'
  168. this.setupPanelElem.style.display = 'none'
  169. this.bodyElem.appendChild(this.setupPanelElem)
  170. this.setupBodyElem = document.createElement('div')
  171. this.setupBodyElem.className = 'bcf_project_setup'
  172. this.setupPanelElem.appendChild(this.setupBodyElem)
  173. this.backSetupButton = Controls.addButton(this.setupBodyElem, 'backSetup', 'button.back', () => this.showTopicList())
  174. this.projectNameElem = Controls.addTextField(this.setupBodyElem, 'project_name', 'bim|label.project_name')
  175. this.saveProjectButtonsElem = document.createElement('div')
  176. this.saveProjectButtonsElem.className = 'bcf_buttons'
  177. this.setupBodyElem.appendChild(this.saveProjectButtonsElem)
  178. this.saveProjectNameButton = Controls.addButton(this.saveProjectButtonsElem, 'saveProjectName', 'button.save', () => this.saveProjectName())
  179. this.extensionsView = Controls.addCodeEditor(this.setupBodyElem, 'extensions_json', 'bim|label.project_extensions', '', { language: 'json', height: '200px' })
  180. this.saveExtensionsButtonsElem = document.createElement('div')
  181. this.saveExtensionsButtonsElem.className = 'bcf_buttons'
  182. this.setupBodyElem.appendChild(this.saveExtensionsButtonsElem)
  183. this.saveExtensionsButton = Controls.addButton(this.saveExtensionsButtonsElem, 'saveExtensions', 'button.save', () => this.saveProjectExtensions())
  184. }
  185. clearTopics() {
  186. this.topicTableElem.tBodies[0].innerHTML = ''
  187. }
  188. showTopicList() {
  189. this.searchPanelElem.style.display = ''
  190. this.detailPanelElem.style.display = 'none'
  191. this.setupPanelElem.style.display = 'none'
  192. if (this.topics === null) {
  193. this.searchTopics()
  194. }
  195. }
  196. showTopic(topic = null, index = -1) {
  197. // index == -1 when topic was created or udpated
  198. this.searchPanelElem.style.display = 'none'
  199. this.detailPanelElem.style.display = ''
  200. this.setupPanelElem.style.display = 'none'
  201. this.tabbedPane.paneElem.style.display = 'none'
  202. if (topic === null || index !== -1) {
  203. this.viewpointListElem.innerHTML = ''
  204. this.commentListElem.innerHTML = ''
  205. this.docRefListElem.innerHTML = ''
  206. }
  207. this.deleteTopicButton.disabled = topic === null
  208. this.index = index
  209. if (this.topics && index !== -1) {
  210. this.topicSearchIndexElem.innerHTML = index + 1 + ' / ' + this.topics.length
  211. this.previousTopicButton.disabled = index === 0
  212. this.nextTopicButton.disabled = index === this.topics.length - 1
  213. } else {
  214. this.topicSearchIndexElem.innerHTML = '?'
  215. this.previousTopicButton.disabled = true
  216. this.nextTopicButton.disabled = true
  217. }
  218. if (topic) {
  219. this.guidElem.value = topic.guid
  220. this.topicIndexElem.value = topic.index
  221. this.titleElem.value = topic.title
  222. this.descriptionElem.value = topic.description
  223. this.dueDateElem.value = this.removeTime(topic.due_date)
  224. this.creationDateElem.value = this.formatDate(topic.creation_date)
  225. this.createdByElem.value = topic.creation_author
  226. this.creationAuthorElem.value = topic.creation_author
  227. this.modifyDateElem.value = this.formatDate(topic.modify_date)
  228. this.modifyAuthorElem.value = topic.modify_author
  229. Controls.setSelectValue(this.topicTypeElem, topic.topic_type)
  230. Controls.setSelectValue(this.priorityElem, topic.priority)
  231. Controls.setSelectValue(this.topicStatusElem, topic.topic_status)
  232. Controls.setSelectValue(this.stageElem, topic.stage)
  233. Controls.setSelectValue(this.assignedToElem, topic.assigned_to)
  234. } else {
  235. this.guidElem.value = null
  236. this.topicIndexElem.value = null
  237. this.titleElem.value = null
  238. this.descriptionElem.value = null
  239. this.dueDateElem.value = null
  240. this.creationDateElem.value = null
  241. this.createdByElem.value = null
  242. this.creationAuthorElem.value = null
  243. this.modifyDateElem.value = null
  244. this.modifyAuthorElem.value = null
  245. Controls.setSelectValue(this.topicTypeElem, null)
  246. Controls.setSelectValue(this.priorityElem, null)
  247. Controls.setSelectValue(this.topicStatusElem, null)
  248. Controls.setSelectValue(this.stageElem, null)
  249. Controls.setSelectValue(this.assignedToElem, null)
  250. }
  251. if (topic) {
  252. this.tabbedPane.paneElem.style.display = ''
  253. if (index !== -1) {
  254. this.loadComments(false, () => this.loadViewpoints(false, () => this.loadDocumentReferences(false)))
  255. }
  256. }
  257. this.commentGuid = null
  258. this.commentElem.value = null
  259. this.docRefGuid = null
  260. this.docRefUrlElem.value = null
  261. this.docRefDescElem.value = null
  262. }
  263. showPreviousTopic() {
  264. if (this.topics) {
  265. let index = this.index
  266. if (index > 0) {
  267. index--
  268. this.showTopic(this.topics[index], index)
  269. }
  270. }
  271. }
  272. showNextTopic() {
  273. if (this.topics) {
  274. let index = this.index
  275. if (index >= 0 && index < this.topics.length - 1) {
  276. index++
  277. this.showTopic(this.topics[index], index)
  278. }
  279. }
  280. }
  281. searchTopics() {
  282. let projectId = this.getProjectId()
  283. if (projectId === null) return
  284. const onCompleted = topics => {
  285. this.hideProgressBar()
  286. this.topics = topics
  287. this.populateTopicTable()
  288. }
  289. const onError = error => {
  290. this.handleError(error, () => this.searchTopics())
  291. }
  292. let filter = {
  293. status: this.statusFilterElem.value,
  294. priority: this.priorityFilterElem.value,
  295. assignedTo: this.assignedToFilterElem.value
  296. }
  297. this.showProgressBar()
  298. this.service.getTopics(projectId, filter, onCompleted, onError)
  299. }
  300. saveTopic() {
  301. const application = this.application
  302. let projectId = this.getProjectId()
  303. let topicGuid = this.guidElem.value
  304. let topic = {
  305. title: this.titleElem.value,
  306. topic_type: this.topicTypeElem.value,
  307. priority: this.priorityElem.value,
  308. topic_status: this.topicStatusElem.value,
  309. stage: this.stageElem.value,
  310. assigned_to: this.assignedToElem.value,
  311. description: this.descriptionElem.value,
  312. due_date: this.addTime(this.dueDateElem.value)
  313. }
  314. const onCompleted = topic => {
  315. this.hideProgressBar()
  316. this.showTopic(topic)
  317. this.topics = null
  318. Toast.create('bim|message.topic_saved')
  319. .setI18N(application.i18n)
  320. .show()
  321. }
  322. const onError = error => {
  323. this.handleError(error, () => this.saveTopic())
  324. }
  325. this.showProgressBar()
  326. if (topicGuid) {
  327. // update
  328. this.service.updateTopic(projectId, topicGuid, topic, onCompleted, onError)
  329. } // creation
  330. else {
  331. this.service.createTopic(projectId, topic, onCompleted, onError)
  332. }
  333. }
  334. deleteTopic() {
  335. const application = this.application
  336. const onCompleted = () => {
  337. this.hideProgressBar()
  338. this.showTopic()
  339. this.topics = null // force topic list refresh
  340. Toast.create('bim|message.topic_deleted')
  341. .setI18N(application.i18n)
  342. .show()
  343. }
  344. const onError = error => {
  345. this.handleError(error, () => this.deleteTopic())
  346. }
  347. let projectId = this.getProjectId()
  348. let topicGuid = this.guidElem.value
  349. if (topicGuid) {
  350. this.showProgressBar()
  351. this.service.deleteTopic(projectId, topicGuid, onCompleted, onError)
  352. }
  353. }
  354. loadViewpoints(scrollBottom, onSuccess) {
  355. const onCompleted = viewpoints => {
  356. this.hideProgressBar()
  357. this.viewpoints = viewpoints
  358. this.populateViewpointList()
  359. if (scrollBottom) {
  360. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  361. }
  362. if (onSuccess) onSuccess()
  363. }
  364. const onError = error => {
  365. this.handleError(error, () => this.loadViewpoints(scrollBottom, onSuccess))
  366. }
  367. let projectId = this.getProjectId()
  368. let topicGuid = this.guidElem.value
  369. this.showProgressBar()
  370. this.service.getViewpoints(projectId, topicGuid, onCompleted, onError)
  371. }
  372. createViewpoint(imageURL = null) {
  373. const application = this.application
  374. const viewpoint = {}
  375. const camera = application.camera
  376. const matrix = camera.matrixWorld
  377. const xAxis = new THREE.Vector3()
  378. const yAxis = new THREE.Vector3()
  379. const zAxis = new THREE.Vector3()
  380. const position = new THREE.Vector3()
  381. matrix.extractBasis(xAxis, yAxis, zAxis)
  382. position.setFromMatrixPosition(matrix)
  383. if (camera instanceof THREE.PerspectiveCamera) {
  384. viewpoint.perspective_camera = {
  385. camera_view_point: { x: position.x, y: position.y, z: position.z },
  386. camera_direction: { x: zAxis.x, y: zAxis.y, z: zAxis.z },
  387. camera_up_vector: { x: yAxis.x, y: yAxis.y, z: yAxis.z },
  388. field_of_view: camera.fov
  389. }
  390. } else if (camera instanceof THREE.OrthographicCamera) {
  391. viewpoint.orthogonal_camera = {
  392. camera_view_point: { x: position.x, y: position.y, z: position.z },
  393. camera_direction: { x: zAxis.x, y: zAxis.y, z: zAxis.z },
  394. camera_up_vector: { x: yAxis.x, y: yAxis.y, z: yAxis.z },
  395. view_to_world_scale: (0.5 * camera.zoom) / camera.right
  396. }
  397. }
  398. const onCompleted = viewpoint => {
  399. this.hideProgressBar()
  400. this.loadViewpoints(true)
  401. Toast.create('bim|message.viewpoint_saved')
  402. .setI18N(application.i18n)
  403. .show()
  404. }
  405. const onError = error => {
  406. this.handleError(error, () => this.createViewpoint(imageURL))
  407. }
  408. let projectId = this.getProjectId()
  409. let topicGuid = this.guidElem.value
  410. if (imageURL === null) {
  411. // capture image from canvas
  412. const canvas = this.application.renderer.domElement
  413. imageURL = canvas.toDataURL('image/png')
  414. }
  415. let format = null
  416. if (imageURL.startsWith('data:image/jpeg;base64,')) {
  417. format = 'jpeg'
  418. } else if (imageURL.startsWith('data:image/png;base64,')) {
  419. format = 'png'
  420. }
  421. if (format) {
  422. const index = imageURL.indexOf(',')
  423. let image = imageURL.substring(index + 1)
  424. viewpoint.snapshot = {
  425. snapshot_type: format,
  426. snapshot_data: image
  427. }
  428. this.showProgressBar()
  429. this.service.createViewpoint(projectId, topicGuid, viewpoint, onCompleted, onError)
  430. } else {
  431. this.handleError({ code: 0, message: 'Unsupported image format' })
  432. }
  433. }
  434. createViewpointFromFile() {
  435. const onChange = () => {
  436. let files = this.inputFile.files
  437. if (files.length > 0) {
  438. let file = files[0]
  439. let reader = new FileReader()
  440. reader.onload = () => {
  441. let imageURL = reader.result
  442. this.createViewpoint(imageURL)
  443. }
  444. reader.readAsDataURL(file)
  445. }
  446. }
  447. let inputFile = document.createElement('input')
  448. this.inputFile = inputFile
  449. inputFile.type = 'file'
  450. inputFile.id = this.name + '_file'
  451. inputFile.accept = '.jpeg, .jpg, .png'
  452. inputFile.addEventListener('change', onChange, false)
  453. inputFile.click()
  454. }
  455. deleteViewpoint(viewpoint) {
  456. const application = this.application
  457. const onCompleted = () => {
  458. this.hideProgressBar()
  459. this.loadViewpoints()
  460. Toast.create('bim|message.viewpoint_deleted')
  461. .setI18N(application.i18n)
  462. .show()
  463. }
  464. const onError = error => {
  465. this.handleError(error, () => this.deleteViewpoint(viewpoint))
  466. }
  467. let projectId = this.getProjectId()
  468. let topicGuid = this.guidElem.value
  469. if (topicGuid) {
  470. this.showProgressBar()
  471. this.service.deleteViewpoint(projectId, topicGuid, viewpoint.guid, onCompleted, onError)
  472. }
  473. }
  474. loadComments(scrollBottom, onSuccess) {
  475. const onCompleted = comments => {
  476. this.hideProgressBar()
  477. this.comments = comments
  478. this.populateCommentList()
  479. if (scrollBottom) {
  480. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  481. }
  482. if (onSuccess) onSuccess()
  483. }
  484. const onError = error => {
  485. this.handleError(error, () => this.loadComments(scrollBottom, onSuccess))
  486. }
  487. let projectId = this.getProjectId()
  488. let topicGuid = this.guidElem.value
  489. this.showProgressBar()
  490. this.service.getComments(projectId, topicGuid, onCompleted, onError)
  491. }
  492. saveComment() {
  493. const application = this.application
  494. let projectId = this.getProjectId()
  495. let topicGuid = this.guidElem.value
  496. let comment = {
  497. comment: this.commentElem.value
  498. }
  499. const onCompleted = comment => {
  500. this.hideProgressBar()
  501. this.commentElem.value = null
  502. this.commentGuid = null
  503. this.loadComments(true)
  504. Toast.create('bim|message.comment_saved')
  505. .setI18N(application.i18n)
  506. .show()
  507. }
  508. const onError = error => {
  509. this.handleError(error, () => this.saveComment())
  510. }
  511. this.showProgressBar()
  512. if (this.commentGuid) {
  513. // update
  514. this.service.updateComment(projectId, topicGuid, this.commentGuid, comment, onCompleted, onError)
  515. } // creation
  516. else {
  517. this.service.createComment(projectId, topicGuid, comment, onCompleted, onError)
  518. }
  519. }
  520. deleteComment(comment) {
  521. const application = this.application
  522. const onCompleted = () => {
  523. this.hideProgressBar()
  524. this.loadComments()
  525. Toast.create('bim|message.comment_deleted')
  526. .setI18N(application.i18n)
  527. .show()
  528. }
  529. const onError = error => {
  530. this.handleError(error, () => this.deleteComment())
  531. }
  532. let projectId = this.getProjectId()
  533. let topicGuid = this.guidElem.value
  534. if (topicGuid) {
  535. this.showProgressBar()
  536. this.service.deleteComment(projectId, topicGuid, comment.guid, onCompleted, onError)
  537. }
  538. }
  539. editComment(comment) {
  540. this.commentGuid = comment.guid
  541. this.commentElem.value = comment.comment
  542. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  543. }
  544. cancelComment() {
  545. this.commentGuid = null
  546. this.commentElem.value = null
  547. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  548. }
  549. loadDocumentReferences(scrollBottom, onSuccess) {
  550. const onCompleted = docRefs => {
  551. this.hideProgressBar()
  552. this.docRefs = docRefs
  553. this.populateDocumentReferenceList()
  554. if (scrollBottom) {
  555. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  556. }
  557. if (onSuccess) onSuccess()
  558. }
  559. const onError = error => {
  560. this.handleError(error, () => this.loadDocumentReferences(scrollBottom, onSuccess))
  561. }
  562. let projectId = this.getProjectId()
  563. let topicGuid = this.guidElem.value
  564. this.showProgressBar()
  565. this.service.getDocumentReferences(projectId, topicGuid, onCompleted, onError)
  566. }
  567. saveDocumentReference() {
  568. const application = this.application
  569. let projectId = this.getProjectId()
  570. let topicGuid = this.guidElem.value
  571. let docRef = {
  572. url: this.docRefUrlElem.value,
  573. description: this.docRefDescElem.value
  574. }
  575. const onCompleted = docRef => {
  576. this.hideProgressBar()
  577. this.docRefUrlElem.value = null
  578. this.docRefDescElem.value = null
  579. this.docRefGuid = null
  580. this.loadDocumentReferences(true)
  581. Toast.create('bim|message.doc_ref_saved')
  582. .setI18N(application.i18n)
  583. .show()
  584. }
  585. const onError = error => {
  586. this.handleError(error, () => this.saveDocumentReference())
  587. }
  588. this.showProgressBar()
  589. if (this.docRefGuid) {
  590. // update
  591. this.service.updateDocumentReference(projectId, topicGuid, this.docRefGuid, docRef, onCompleted, onError)
  592. } // creation
  593. else {
  594. this.service.createDocumentReference(projectId, topicGuid, docRef, onCompleted, onError)
  595. }
  596. }
  597. editDocumentReference(docRef) {
  598. this.docRefGuid = docRef.guid
  599. this.docRefUrlElem.value = docRef.url
  600. this.docRefDescElem.value = docRef.description
  601. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  602. }
  603. cancelDocumentReference() {
  604. this.docRefGuid = null
  605. this.docRefUrlElem.value = null
  606. this.docRefDescElem.value = null
  607. this.detailPanelElem.scrollTop = this.detailPanelElem.scrollHeight
  608. }
  609. deleteDocumentReference(docRef) {
  610. const application = this.application
  611. const onCompleted = () => {
  612. this.hideProgressBar()
  613. this.loadDocumentReferences()
  614. Toast.create('bim|message.doc_ref_deleted')
  615. .setI18N(application.i18n)
  616. .show()
  617. }
  618. const onError = error => {
  619. this.handleError(error, () => this.deleteDocumentReference(docRef))
  620. }
  621. let projectId = this.getProjectId()
  622. let topicGuid = this.guidElem.value
  623. if (topicGuid) {
  624. this.showProgressBar()
  625. this.service.deleteDocumentReference(projectId, topicGuid, docRef.guid, onCompleted, onError)
  626. }
  627. }
  628. updateExtensions() {
  629. let projectId = this.getProjectId()
  630. if (projectId === null) return
  631. const onCompleted = extensions => {
  632. this.hideProgressBar()
  633. this.extensions = extensions
  634. this.populateExtensions()
  635. }
  636. const onError = error => {
  637. this.handleError(error, () => this.updateExtensions())
  638. }
  639. this.showProgressBar()
  640. this.service.getExtensions(projectId, onCompleted, onError)
  641. }
  642. refreshProjects() {
  643. const projects = []
  644. const onCompleted = serverProjects => {
  645. this.hideProgressBar()
  646. this.filterPanelElem.style.display = ''
  647. this.topicTableElem.style.display = ''
  648. const projectIdSet = new Set()
  649. for (let serverProject of serverProjects) {
  650. let projectId = serverProject.project_id
  651. let projectName = serverProject.name
  652. projectIdSet.add(projectId)
  653. projects.push([projectId, projectName])
  654. }
  655. const scene = this.application.scene
  656. scene.traverse(object => {
  657. if (object._ifc && object._ifc.constructor.name === 'IfcProject') {
  658. let projectId = object._ifc.GlobalId
  659. let projectName = object._ifc.Name || object._ifc.LongName
  660. if (!projectIdSet.has(projectId)) {
  661. projects.push([projectId, projectName])
  662. }
  663. }
  664. })
  665. Controls.setSelectOptions(this.projectElem, projects)
  666. const disabled = projects.length === 0
  667. this.searchTopicsButton.disabled = disabled
  668. this.setupProjectButton.disabled = disabled
  669. this.searchNewTopicButton.disabled = disabled
  670. this.updateExtensions()
  671. }
  672. const onError = error => {
  673. this.handleError(error, () => this.refreshProjects())
  674. }
  675. this.clearTopics()
  676. this.showProgressBar()
  677. this.service.getProjects(onCompleted, onError)
  678. }
  679. saveProjectName() {
  680. const application = this.application
  681. const projectName = this.projectNameElem.value
  682. const index = this.projectElem.selectedIndex
  683. const options = this.projectElem.options
  684. const oldProjectName = options[index].label
  685. if (projectName !== oldProjectName) {
  686. const onCompleted = project => {
  687. this.hideProgressBar()
  688. options[index].label = project.name
  689. Toast.create('bim|message.project_saved')
  690. .setI18N(application.i18n)
  691. .show()
  692. }
  693. const onError = error => {
  694. this.handleError(error, () => this.saveProjectName())
  695. }
  696. const projectId = this.getProjectId()
  697. const project = {
  698. name: projectName
  699. }
  700. this.showProgressBar()
  701. this.service.updateProject(projectId, project, onCompleted, onError)
  702. }
  703. }
  704. saveProjectExtensions() {
  705. const application = this.application
  706. const extensionsText = this.extensionsView.state.doc.toString()
  707. const oldExtensionsText = JSON.stringify(this.extensions, null, 2)
  708. if (extensionsText !== oldExtensionsText) {
  709. const onCompleted = extensions => {
  710. this.hideProgressBar()
  711. this.extensions = extensions
  712. this.populateExtensions()
  713. Toast.create('bim|message.project_extensions_saved')
  714. .setI18N(application.i18n)
  715. .show()
  716. }
  717. const onError = error => {
  718. this.handleError(error, () => this.saveProjectExtensions())
  719. }
  720. try {
  721. let extensions = JSON.parse(extensionsText)
  722. const projectId = this.getProjectId()
  723. this.showProgressBar()
  724. this.service.updateExtensions(projectId, extensions, onCompleted, onError)
  725. } catch (ex) {
  726. console.error(ex)
  727. }
  728. }
  729. }
  730. showViewpoint(viewpoint) {
  731. const application = this.application
  732. let position, dir, up, camera
  733. if (viewpoint.perspective_camera) {
  734. const pcam = viewpoint.perspective_camera
  735. position = pcam.camera_view_point
  736. dir = pcam.camera_direction
  737. up = pcam.camera_up_vector
  738. let fov = pcam.field_of_view
  739. camera = this.application.perspectiveCamera
  740. camera.fov = fov
  741. } else {
  742. const ocam = viewpoint.orthogonal_camera
  743. position = ocam.camera_view_point
  744. dir = ocam.camera_direction
  745. up = ocam.camera_up_vector
  746. let scale = ocam.view_to_world_scale
  747. camera = this.application.orthographicCamera
  748. camera.zoom = scale
  749. camera.right = 0.5
  750. camera.left = -0.5
  751. camera.top = 0.1
  752. camera.bottom = -0.1
  753. camera.near = -100
  754. camera.far = 100
  755. }
  756. const xAxis = new THREE.Vector3()
  757. const yAxis = new THREE.Vector3(up.x, up.y, up.z)
  758. const zAxis = new THREE.Vector3(dir.x, dir.y, dir.z)
  759. xAxis.crossVectors(yAxis, zAxis).normalize()
  760. yAxis.normalize()
  761. zAxis.normalize()
  762. camera.matrix.makeBasis(xAxis, yAxis, zAxis)
  763. camera.matrix.setPosition(position.x, position.y, position.z)
  764. camera.matrix.decompose(camera.position, camera.quaternion, camera.scale)
  765. camera.updateMatrixWorld(true)
  766. application.notifyObjectsChanged(camera, this)
  767. application.activateCamera(camera)
  768. }
  769. showProjectSetup() {
  770. this.searchPanelElem.style.display = 'none'
  771. this.detailPanelElem.style.display = 'none'
  772. this.setupPanelElem.style.display = ''
  773. const index = this.projectElem.selectedIndex
  774. const options = this.projectElem.options
  775. this.projectNameElem.value = options[index].label
  776. const json = JSON.stringify(this.extensions, null, 2)
  777. const state = this.extensionsView.state
  778. const tx = state.update({ changes: { from: 0, to: state.doc.length, insert: json } })
  779. this.extensionsView.dispatch(tx)
  780. }
  781. populateTopicTable() {
  782. const topics = this.topics
  783. const topicsElem = this.topicTableElem
  784. topicsElem.tBodies[0].innerHTML = ''
  785. for (let i = 0; i < topics.length; i++) {
  786. let topic = topics[i]
  787. let rowElem = Controls.addTableRow(topicsElem)
  788. const openTopic = () => {
  789. this.showTopic(topic, i)
  790. }
  791. Controls.addLink(rowElem.children[0], topic.index, '#', null, null, openTopic)
  792. Controls.addLink(rowElem.children[1], topic.title, '#', null, null, openTopic)
  793. rowElem.children[2].innerHTML = topic.topic_status
  794. }
  795. }
  796. populateCommentList() {
  797. const comments = this.comments
  798. const commentsElem = this.commentListElem
  799. commentsElem.innerHTML = ''
  800. for (let comment of comments) {
  801. let itemListElem = document.createElement('li')
  802. let itemListHeaderElem = document.createElement('div')
  803. itemListElem.appendChild(itemListHeaderElem)
  804. let spanElem = document.createElement('span')
  805. spanElem.className = 'icon comment'
  806. itemListHeaderElem.appendChild(spanElem)
  807. let authorDate = comment.author || 'anonymous'
  808. if (comment.date) {
  809. authorDate += ' (' + this.formatDate(comment.date) + ')'
  810. }
  811. authorDate += ':'
  812. Controls.addText(itemListHeaderElem, authorDate, 'bcf_comment_author')
  813. Controls.addButton(itemListHeaderElem, 'updateComment', 'button.edit', () => this.editComment(comment), 'bcf_edit_comment')
  814. Controls.addButton(
  815. itemListHeaderElem,
  816. 'deleteComment',
  817. 'button.delete',
  818. () => {
  819. ConfirmDialog.create('bim|title.delete_comment', 'bim|question.delete_comment')
  820. .setAction(() => this.deleteComment(comment))
  821. .setAcceptLabel('button.delete')
  822. .setI18N(this.application.i18n)
  823. .show()
  824. },
  825. 'bcf_delete_comment'
  826. )
  827. Controls.addText(itemListElem, comment.comment, 'bcf_comment_text')
  828. this.application.i18n.updateTree(itemListElem)
  829. commentsElem.appendChild(itemListElem)
  830. }
  831. }
  832. populateDocumentReferenceList() {
  833. const docRefs = this.docRefs
  834. const docRefsElem = this.docRefListElem
  835. docRefsElem.innerHTML = ''
  836. for (let docRef of docRefs) {
  837. let itemListElem = document.createElement('li')
  838. let spanElem = document.createElement('span')
  839. spanElem.className = 'icon doc_ref'
  840. itemListElem.appendChild(spanElem)
  841. let linkElem = document.createElement('a')
  842. linkElem.href = docRef.url
  843. linkElem.target = '_blank'
  844. linkElem.innerHTML = docRef.description
  845. itemListElem.appendChild(linkElem)
  846. Controls.addButton(itemListElem, 'updateDocRef', 'button.edit', () => this.editDocumentReference(docRef), 'bcf_edit_doc_ref')
  847. Controls.addButton(
  848. itemListElem,
  849. 'deleteDocRef',
  850. 'button.delete',
  851. () => {
  852. ConfirmDialog.create('bim|title.delete_doc_ref', 'bim|question.delete_doc_ref')
  853. .setAction(() => this.deleteDocumentReference(docRef))
  854. .setAcceptLabel('button.delete')
  855. .setI18N(this.application.i18n)
  856. .show()
  857. },
  858. 'bcf_delete_doc_ref'
  859. )
  860. this.application.i18n.updateTree(itemListElem)
  861. docRefsElem.appendChild(itemListElem)
  862. }
  863. }
  864. populateViewpointList() {
  865. let projectId = this.getProjectId()
  866. let topicGuid = this.guidElem.value
  867. const viewpoints = this.viewpoints
  868. const viewpointsElem = this.viewpointListElem
  869. viewpointsElem.innerHTML = ''
  870. for (let viewpoint of viewpoints) {
  871. let itemListElem = document.createElement('li')
  872. let spanElem = document.createElement('span')
  873. spanElem.className = 'icon viewpoint'
  874. itemListElem.appendChild(spanElem)
  875. let vpType = ''
  876. if (viewpoint.perspective_camera) {
  877. vpType = ' (P)'
  878. } else if (viewpoint.orthogonal_camera) {
  879. vpType += ' (O)'
  880. }
  881. Controls.addTextWithArgs(itemListElem, 'bim|message.viewpoint', [viewpoint.index || '', vpType], 'bcf_viewpoint_text')
  882. Controls.addButton(itemListElem, 'showViewpoint', 'button.view', () => this.showViewpoint(viewpoint), 'bcf_show_viewpoint')
  883. Controls.addButton(
  884. itemListElem,
  885. 'deleteViewpoint',
  886. 'button.delete',
  887. () => {
  888. ConfirmDialog.create('bim|title.delete_viewpoint', 'bim|question.delete_viewpoint')
  889. .setAction(() => this.deleteViewpoint(viewpoint))
  890. .setAcceptLabel('button.delete')
  891. .setI18N(this.application.i18n)
  892. .show()
  893. },
  894. 'bcf_delete_viewpoint'
  895. )
  896. this.application.i18n.updateTree(itemListElem)
  897. if (viewpoint.snapshot) {
  898. let type = viewpoint.snapshot.snapshot_type
  899. let data = viewpoint.snapshot.snapshot_data
  900. let source
  901. if (data) {
  902. source = type === 'png' ? 'data:image/png;base64,' + data : 'data:image/jpeg;base64,' + data
  903. } else {
  904. source = this.service.url + '/bcf/2.1/projects/' + projectId + '/topics/' + topicGuid + '/viewpoints/' + viewpoint.guid + '/snapshot'
  905. }
  906. let linkElem = Controls.addLink(itemListElem, null, '#', 'bim|label.zoom_image', 'viewpoint_snapshot', () => this.zoomSnapshot(source))
  907. let imageElem = document.createElement('img')
  908. imageElem.className = 'viewpoint_snapshot'
  909. imageElem.src = source
  910. linkElem.appendChild(imageElem)
  911. }
  912. viewpointsElem.appendChild(itemListElem)
  913. }
  914. }
  915. zoomSnapshot(source) {
  916. const dialog = new Dialog('bim|title.viewpoint')
  917. dialog.setI18N(this.application.i18n)
  918. dialog.setClassName('viewpoint_dialog')
  919. let imageElem = document.createElement('img')
  920. imageElem.className = 'snapshot_zoom'
  921. imageElem.src = source
  922. imageElem.onload = () => {
  923. const container = this.application.container
  924. let imageWidth = imageElem.width
  925. let imageHeight = imageElem.height
  926. let imageAspectRatio = imageWidth / imageHeight
  927. let screenAspectRatio = container.clientWidth / container.clientHeight
  928. if (imageAspectRatio > screenAspectRatio) {
  929. let width = container.clientWidth
  930. dialog.setSize(width, width / imageAspectRatio + 100)
  931. } else {
  932. let height = container.clientHeight
  933. dialog.setSize((height - 100) * imageAspectRatio, height)
  934. }
  935. let acceptButton = dialog.addButton('accept', 'button.close', () => {
  936. dialog.hide()
  937. })
  938. dialog.onShow = () => acceptButton.focus()
  939. dialog.show()
  940. }
  941. dialog.bodyElem.appendChild(imageElem)
  942. }
  943. populateExtensions() {
  944. const ext = this.extensions
  945. Controls.setSelectOptions(this.statusFilterElem, [''].concat(ext.topic_status))
  946. Controls.setSelectOptions(this.priorityFilterElem, [''].concat(ext.priority))
  947. Controls.setSelectOptions(this.assignedToFilterElem, [''].concat(ext.user_id_type))
  948. Controls.setSelectOptions(this.topicTypeElem, ext.topic_type)
  949. Controls.setSelectOptions(this.priorityElem, ext.priority)
  950. Controls.setSelectOptions(this.topicStatusElem, ext.topic_status)
  951. Controls.setSelectOptions(this.stageElem, ext.stage)
  952. Controls.setSelectOptions(this.assignedToElem, ext.user_id_type)
  953. }
  954. getProjectId() {
  955. let projectId = this.projectElem.value
  956. if (projectId === '') projectId = null
  957. return projectId
  958. }
  959. changeProject() {
  960. this.clearTopics()
  961. this.updateExtensions()
  962. }
  963. formatDate(dateString) {
  964. if (dateString === null || dateString === '') return null
  965. const index = dateString.indexOf('T')
  966. return index === -1 ? dateString : dateString.substring(0, index) + ' ' + dateString.substring(index + 1)
  967. }
  968. addTime(dateString) {
  969. if (dateString === null || dateString === '') return null
  970. return dateString + 'T00:00:00'
  971. }
  972. removeTime(dateString) {
  973. if (dateString === null || dateString === '') return null
  974. const index = dateString.indexOf('T')
  975. return dateString.substring(0, index)
  976. }
  977. onShow() {
  978. this.updateServices()
  979. }
  980. updateServices() {
  981. const application = this.application
  982. const services = application.services[this.group]
  983. let options = []
  984. for (let name in services) {
  985. let service = services[name]
  986. options.push([service.name, service.description || service.name])
  987. }
  988. Controls.setSelectOptions(this.bcfServiceElem, options)
  989. if (options.length > 0) {
  990. let name = this.bcfServiceElem.value
  991. this.service = application.services[this.group][name]
  992. } else {
  993. this.service = null
  994. }
  995. let service = this.service
  996. this.connectButton.style.display = service ? '' : 'none'
  997. this.addServiceButton.style.display = ''
  998. this.editServiceButton.style.display = service ? '' : 'none'
  999. this.deleteServiceButton.style.display = service ? '' : 'none'
  1000. }
  1001. showAddDialog() {
  1002. let serviceTypes = ServiceManager.getTypesOf(BCFService)
  1003. let dialog = new ServiceDialog('Add BCF service', serviceTypes)
  1004. dialog.serviceTypeSelect.disabled = true
  1005. dialog.setI18N(this.application.i18n)
  1006. dialog.onSave = (serviceType, name, description, url, username, password) => {
  1007. const service = new ServiceManager.classes[serviceType]()
  1008. service.name = name
  1009. service.description = description
  1010. service.url = url
  1011. service.username = username
  1012. service.password = password
  1013. this.application.addService(service, this.group)
  1014. this.updateServices()
  1015. this.service = service
  1016. this.bcfServiceElem.value = name
  1017. this.filterPanelElem.style.display = 'none'
  1018. this.topicTableElem.style.display = 'none'
  1019. }
  1020. dialog.show()
  1021. }
  1022. showEditDialog() {
  1023. if (this.service === null) return
  1024. const service = this.service
  1025. let serviceTypes = ServiceManager.getTypesOf(BCFService)
  1026. let dialog = new ServiceDialog('Edit BCF service', serviceTypes, service.constructor.type, service.name, service.description, service.url, service.username, service.password)
  1027. dialog.serviceTypeSelect.disabled = true
  1028. dialog.setI18N(this.application.i18n)
  1029. dialog.nameElem.readOnly = true
  1030. dialog.onSave = (serviceType, name, description, url, username, password) => {
  1031. service.description = description
  1032. service.url = url
  1033. service.username = username
  1034. service.password = password
  1035. this.application.addService(service, this.group)
  1036. this.updateServices()
  1037. this.filterPanelElem.style.display = 'none'
  1038. this.topicTableElem.style.display = 'none'
  1039. }
  1040. dialog.show()
  1041. }
  1042. showDeleteDialog() {
  1043. const application = this.application
  1044. let name = this.bcfServiceElem.value
  1045. if (name) {
  1046. ConfirmDialog.create('bim|title.delete_bcf_service', 'bim|question.delete_bcf_service', name)
  1047. .setAction(() => {
  1048. let service = application.services[this.group][name]
  1049. application.removeService(service, this.group)
  1050. this.updateServices()
  1051. this.filterPanelElem.style.display = 'none'
  1052. this.topicTableElem.style.display = 'none'
  1053. })
  1054. .setAcceptLabel('button.delete')
  1055. .setI18N(application.i18n)
  1056. .show()
  1057. }
  1058. }
  1059. handleError(error, onLogin) {
  1060. this.hideProgressBar()
  1061. if (error.code === 401) {
  1062. this.requestCredentials('message.invalid_credentials', onLogin)
  1063. } else if (error.code === 403) {
  1064. this.requestCredentials('message.action_denied', onLogin)
  1065. } else {
  1066. let message = error.message
  1067. MessageDialog.create('ERROR', message)
  1068. .setClassName('error')
  1069. .setI18N(this.application.i18n)
  1070. .show()
  1071. }
  1072. }
  1073. requestCredentials(message, onLogin, onFailed) {
  1074. const loginDialog = new LoginDialog(this.application, message)
  1075. loginDialog.login = (username, password) => {
  1076. this.service.username = username
  1077. this.service.password = password
  1078. if (onLogin) onLogin()
  1079. }
  1080. loginDialog.onCancel = () => {
  1081. loginDialog.hide()
  1082. if (onFailed) onFailed()
  1083. }
  1084. loginDialog.show()
  1085. }
  1086. showProgressBar() {
  1087. this.application.progressBar.message = ''
  1088. this.application.progressBar.progress = undefined
  1089. this.application.progressBar.visible = true
  1090. }
  1091. hideProgressBar() {
  1092. this.application.progressBar.visible = false
  1093. }
  1094. }
  1095. export { BCFPanel }