1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | import FaceSelection from './FaceSelection'
|
15 | import ProgressImage from './faces/ProgressImage'
|
16 | import ImageFace from './faces/ImageFace'
|
17 | import ImagePolicy from './faces/ImagePolicy'
|
18 | import LayeredImage from './faces/LayeredImage'
|
19 | import BaseWebFace from './faces/WebFace/BaseWebFace'
|
20 |
|
21 |
|
22 | let registeredFace = {
|
23 | 'native://image': ImageFace,
|
24 | 'native://progress-image-overlay': ProgressImage,
|
25 | 'native://image-policy': ImagePolicy,
|
26 | 'native://layered-image': LayeredImage
|
27 | }
|
28 |
|
29 | export default class VatomView {
|
30 | constructor (bv, vAtom, FSP, config) {
|
31 | this.blockv = bv
|
32 | this.vatomObj = vAtom
|
33 | this.fsp = FSP || FaceSelection.Icon
|
34 | this.config = config || {}
|
35 |
|
36 | this._currentFace = null
|
37 | this.onVatomUpdated = this.onVatomUpdated.bind(this)
|
38 | this.region = this.blockv.dataPool.region('inventory')
|
39 | this.region.addEventListener('object.updated', this.onVatomUpdated)
|
40 |
|
41 |
|
42 | this.element = document.createElement('div')
|
43 | this.element.style.position = 'relative'
|
44 | this.element.style.width = this.config.width || '64px'
|
45 | this.element.style.height = this.config.height || '64px'
|
46 |
|
47 |
|
48 | this.createLoader = this.config.loader || function () {
|
49 | let css = '.spinner {margin: 0px auto;width: 70px;text-align: center; margin-top: -50%;}'
|
50 | css += '.spinner > div {width: 12px;height: 12px;margin: 0px 3px;border-radius: 100%;display: inline-block;-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;animation: sk-bouncedelay 1.4s infinite ease-in-out both;}'
|
51 | css += '.spinner .bounce1 {-webkit-animation-delay: -0.32s;animation-delay: -0.32s;}'
|
52 | css += '.spinner .bounce2 {-webkit-animation-delay: -0.16s;animation-delay: -0.16s;}'
|
53 | css += '@-webkit-keyframes sk-bouncedelay {0%, 80%, 100% { -webkit-transform: scale(0) }40% { -webkit-transform: scale(1.0) }}'
|
54 | css += '@keyframes sk-bouncedelay {0%, 80%, 100% {-webkit-transform: scale(0);transform: scale(0);} 40% {-webkit-transform: scale(1.0);transform: scale(1.0);}}'
|
55 |
|
56 | let head = document.head || document.getElementsByTagName('head')[0]
|
57 | let style = document.createElement('style')
|
58 | head.appendChild(style)
|
59 |
|
60 | style.type = 'text/css'
|
61 | if (style.styleSheet) {
|
62 |
|
63 | style.styleSheet.cssText = css
|
64 | } else {
|
65 | style.appendChild(document.createTextNode(css))
|
66 | }
|
67 |
|
68 | let loader = document.createElement('div')
|
69 |
|
70 | loader.innerHTML = '<div class="spinner"><div class="bounce1" style="background-color: #333;"></div><div class="bounce2" style="background-color: #333;"></div><div class="bounce3" style="background-color: #333;"></div></div>'
|
71 | return loader
|
72 | }
|
73 |
|
74 | this.createErrorView = this.config.errorView || function (bvi, v, err) {
|
75 | let con = document.createElement('div')
|
76 | const rs = v.properties.resources.find(r => r.name === 'ActivatedImage')
|
77 | const du = rs && bvi.UserManager.encodeAssetProvider(rs.value.value)
|
78 | con.style.backgroundSize = 'contain'
|
79 | con.style.backgroundPosition = 'center'
|
80 | con.style.backgroundRepeat = 'no-repeat'
|
81 | con.style.backgroundImage = `url('${du}')`
|
82 | con.style.width = '100%'
|
83 | con.style.height = '100%'
|
84 |
|
85 | let errorView = document.createElement('div')
|
86 | errorView.style.cssText = 'position: absolute; top: 0px; right: 0px; padding-right: 5px; padding-top: 5px;'
|
87 | errorView.innerHTML = '<img width="20" height="20" src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ5Ny40NzIgNDk3LjQ3MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDk3LjQ3MiA0OTcuNDcyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KPGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4yNSAwIDAgLTEuMjUgMCA0NSkiPgoJPGc+CgkJPGc+CgkJCTxwYXRoIHN0eWxlPSJmaWxsOiNGRkNDNEQ7IiBkPSJNMjQuMzc0LTM1Ny44NTdjLTIwLjk1OCwwLTMwLjE5NywxNS4yMjMtMjAuNTQ4LDMzLjgyNkwxODEuNDIxLDE3LjkyOCAgICAgYzkuNjQ4LDE4LjYwMywyNS40NjMsMTguNjAzLDM1LjEyMywwTDM5NC4xNC0zMjQuMDMxYzkuNjcxLTE4LjYwMywwLjQyMS0zMy44MjYtMjAuNTQ4LTMzLjgyNkgyNC4zNzR6Ii8+CgkJCTxwYXRoIHN0eWxlPSJmaWxsOiMyMzFGMjA7IiBkPSJNMTczLjYwNS04MC45MjJjMCwxNC44MTQsMTAuOTM0LDIzLjk4NCwyNS4zOTUsMjMuOTg0YzE0LjEyLDAsMjUuNDA3LTkuNTEyLDI1LjQwNy0yMy45ODQgICAgIFYtMjE2Ljc1YzAtMTQuNDYxLTExLjI4Ny0yMy45ODQtMjUuNDA3LTIzLjk4NGMtMTQuNDYxLDAtMjUuMzk1LDkuMTgyLTI1LjM5NSwyMy45ODRWLTgwLjkyMnogTTE3MS40ODktMjg5LjA1NiAgICAgYzAsMTUuMTY3LDEyLjM0NSwyNy41MTEsMjcuNTExLDI3LjUxMWMxNS4xNjcsMCwyNy41MjMtMTIuMzQ1LDI3LjUyMy0yNy41MTFjMC0xNS4xNzgtMTIuMzU2LTI3LjUyMy0yNy41MjMtMjcuNTIzICAgICBDMTgzLjgzNC0zMTYuNTc5LDE3MS40ODktMzA0LjIzNCwxNzEuNDg5LTI4OS4wNTYiLz4KCQk8L2c+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==" />'
|
88 | errorView.addEventListener('click', e => alert(err.message), false)
|
89 |
|
90 | con.appendChild(errorView)
|
91 | return con
|
92 | }
|
93 |
|
94 | this.update()
|
95 | }
|
96 |
|
97 | update () {
|
98 |
|
99 |
|
100 | if (!this.vatomObj)
|
101 | return console.warn('No vAtom supplied')
|
102 |
|
103 |
|
104 | if (this._currentFace && this._currentFace.onUnload) this._currentFace.onUnload()
|
105 | this._currentFace = null
|
106 |
|
107 |
|
108 | const view = this.element
|
109 | while (view.firstChild) {
|
110 | view.removeChild(view.firstChild)
|
111 | }
|
112 |
|
113 |
|
114 | this.load()
|
115 |
|
116 | }
|
117 |
|
118 | load () {
|
119 |
|
120 | if (this.loader && this.loader.parentNode) {
|
121 | this.loader.parentNode.removeChild(this.loader)
|
122 | }
|
123 | if (this.errorView && this.errorView.parentNode) {
|
124 | this.errorView.parentNode.removeChild(this.errorView)
|
125 | }
|
126 | this.loader = null
|
127 | this.errorView = null
|
128 |
|
129 | let rFace = null
|
130 | Promise.resolve(() => null).then(() => {
|
131 |
|
132 | const st = this.fsp(this.vatomObj)
|
133 | if (!st)
|
134 | throw new Error('No face found for this view mode.')
|
135 | let FaceClass = null
|
136 |
|
137 | const du = st.properties.display_url.toLowerCase()
|
138 | let excludedFaces = this.config.excludedFaces
|
139 |
|
140 | if (excludedFaces.includes(du)) {
|
141 | throw new Error('This face is not allowed to run in this view mode. [excluded : ' + du + ']')
|
142 | } else {
|
143 | FaceClass = registeredFace[du]
|
144 | }
|
145 |
|
146 | if (FaceClass === undefined && du.indexOf('http') !== -1) {
|
147 | FaceClass = BaseWebFace
|
148 | } else if (FaceClass === undefined) {
|
149 | throw new Error('No Face Registered')
|
150 | }
|
151 |
|
152 | rFace = new FaceClass(this, this.vatomObj, st)
|
153 | this._currentFace = rFace
|
154 |
|
155 | rFace.element.style.opacity = 0
|
156 |
|
157 |
|
158 | this.element.appendChild(rFace.element)
|
159 |
|
160 |
|
161 | this.element.appendChild(this.loader = this.createLoader())
|
162 |
|
163 |
|
164 |
|
165 |
|
166 | return rFace.onLoad()
|
167 | }).then(() => {
|
168 | if (this.loader) {
|
169 | this.element.removeChild(this.loader)
|
170 | rFace.element.style.opacity = 1
|
171 | }
|
172 | }).catch((err) => {
|
173 | console.warn('Error from catch', err)
|
174 |
|
175 | this.element.appendChild(this.errorView = this.createErrorView(this.blockv, this.vatom, err))
|
176 | if (rFace && rFace.element && rFace.element.parentNode) {
|
177 | this.element.removeChild(rFace.element)
|
178 | }
|
179 | if (this.loader && this.loader.parentNode) {
|
180 | this.element.removeChild(this.loader)
|
181 | }
|
182 | })
|
183 | }
|
184 |
|
185 | set vatom (vAtom) {
|
186 | if (vAtom && vAtom.id === this.vatomObj.id) {
|
187 | this.vatomObj.payload = vAtom.payload
|
188 | if (this._currentFace) {
|
189 | this._currentFace.onVatomUpdated()
|
190 | }
|
191 | } else if (vAtom) {
|
192 | this.vatomObj = vAtom
|
193 | this.update()
|
194 | }
|
195 | }
|
196 |
|
197 | get vatom () {
|
198 | return this.vatomObj
|
199 | }
|
200 |
|
201 | free () {
|
202 |
|
203 |
|
204 | this.region.removeEventListener('object.updated', this.onVatomUpdated)
|
205 |
|
206 |
|
207 | if (this._currentFace && this._currentFace.onUnload) this._currentFace.onUnload()
|
208 | this._currentFace = null
|
209 |
|
210 |
|
211 | const view = this.element
|
212 | while (view.firstChild) {
|
213 | view.removeChild(view.firstChild)
|
214 | }
|
215 |
|
216 | }
|
217 |
|
218 | onVatomUpdated (id) {
|
219 |
|
220 | if (id !== this.vatomObj.id) {
|
221 | return
|
222 | }
|
223 |
|
224 |
|
225 | var vatom = this.region.getItem(id, false)
|
226 | if (!vatom)
|
227 | return console.warn('DataPool indicated that an updated vatom was available, but we were unable to fetch it.')
|
228 |
|
229 |
|
230 | this.vatom = vatom
|
231 | }
|
232 |
|
233 |
|
234 | static registerFace (faceClass) {
|
235 | registeredFace[faceClass.url.toLowerCase()] = faceClass
|
236 | }
|
237 | }
|