Mercurial > xmpp-account-manager
annotate avatar.js @ 2:db033e5eabcb
Add pubsub#access_model configuration for avatars.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Sat, 22 Dec 2018 01:21:03 +0100 |
parents | d6df73b466f6 |
children | 5aa1bf7154b0 |
rev | line source |
---|---|
0 | 1 'use strict'; |
2 | |
3 function initAvatar(connection) { | |
4 const DEFAULT_AVATAR = 'data:image/svg+xml,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 150 150"><rect width="150" height="150" fill="#888" stroke-width="1" stroke="#000"/><text x="75" y="100" text-anchor="middle" font-size="100">?</text></svg>'; | |
5 | |
6 const avatar_data = {}; | |
7 const avatar_img = document.getElementById('avatar'); | |
8 const avatar_size = document.getElementById('avatar-size'); | |
9 const avatar_file = document.getElementById('avatar-file'); | |
10 const avatar_upload = document.getElementById('avatar-upload'); | |
11 const avatar_change = document.getElementById('avatar-change'); | |
2
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
12 const avatar_access = document.getElementById('avatar-access'); |
0 | 13 |
14 avatar_img.src = DEFAULT_AVATAR; | |
15 const iq = $iq({type: 'get'}) | |
16 .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub'}) | |
17 .c('items', {node: 'urn:xmpp:avatar:metadata'}); | |
18 connection.sendIQ(iq, onAvatarMetadata, onAvatarRetrievalError.bind(null, 'PubSub metadata query failed.')); | |
19 | |
20 function onAvatarMetadata(result_iq) | |
21 { | |
22 const item = parseXPath(result_iq, './pubsub:pubsub/pubsub:items/pubsub:item'); | |
23 if (item == null) | |
24 return onAvatarRetrievalError('no item found.'); | |
25 const id = item.getAttributeNS(null, 'id'); | |
26 const info = parseXPath(item, './avatar_metadata:metadata/avatar_metadata:info'); | |
27 if (info == null) | |
28 return onAvatarRetrievalError('no info found, your avatar metadata node is broken.'); | |
29 if (id != info.getAttributeNS(null, 'id')) | |
30 return onAvatarRetrievalError('invalid id in metadata.'); | |
31 | |
32 const parsed_info = { | |
33 id: id, | |
34 type: info.getAttributeNS(null, 'type'), | |
35 bytes: info.getAttributeNS(null, 'bytes'), | |
36 width: info.getAttributeNS(null, 'width'), | |
37 height: info.getAttributeNS(null, 'height'), | |
38 }; | |
39 const iq = $iq({type: 'get'}) | |
40 .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub'}) | |
41 .c('items', {node: 'urn:xmpp:avatar:data'}) | |
42 .c('item', {id: id}); | |
43 connection.sendIQ(iq, onAvatarData.bind(null, parsed_info), onAvatarRetrievalError.bind(null, 'PubSub data query failed.')); | |
44 } | |
45 | |
46 function onAvatarData(info, result_iq) | |
47 { | |
48 const item = parseXPath(result_iq, './pubsub:pubsub/pubsub:items/pubsub:item'); | |
49 if (item == null) | |
50 return onAvatarRetrievalError('no item found.'); | |
51 if (info.id != item.getAttributeNS(null, 'id')) | |
52 return onAvatarRetrievalError('invalid id in data.'); | |
53 | |
54 const data = parseXPath(item, './avatar_data:data').textContent; | |
55 const url = 'data:' + info.type + ';base64,' + data; | |
56 // TODO: validate the bytes too. | |
57 /* | |
58 // TODO: figure out why this didn’t work. | |
59 avatar_img.onload = function (evt) { | |
60 const img = evt.target; | |
61 if (img.naturalWidth != info.width || img.naturalHeight != info.height) | |
62 return onAvatarRetrievalError('invalid width or height in image data.'); | |
63 avatar_img.onload = null; | |
64 }; | |
65 */ | |
66 avatar_img.src = url; | |
67 } | |
68 | |
69 function onAvatarRetrievalError(string) | |
70 { | |
71 console.log('Failed to retrieve avatar, an empty one is displayed instead: ' + string); | |
72 avatar_img.src = DEFAULT_AVATAR; | |
73 } | |
74 | |
75 avatar_upload.addEventListener('click', function (evt) { | |
76 avatar_file.click(); | |
77 }); | |
78 | |
79 avatar_change.addEventListener('click', function (evt) { | |
80 const metadata_iq = $iq({type: 'set'}) | |
81 .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub'}) | |
82 .c('publish', {node: 'urn:xmpp:avatar:metadata'}) | |
83 .c('item', {id: avatar_data.id}) | |
84 .c('metadata', {xmlns: 'urn:xmpp:avatar:metadata'}) | |
85 .c('info', { | |
86 id: avatar_data.id, | |
87 type: avatar_data.type, | |
88 bytes: avatar_data.bytes, | |
89 width: avatar_img.naturalWidth, | |
90 height: avatar_img.naturalHeight, | |
91 }); | |
92 connection.sendIQ(metadata_iq, onAvatarMetadataUpload, onAvatarUploadError); | |
93 const data_iq = $iq({type: 'set'}) | |
94 .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub'}) | |
95 .c('publish', {node: 'urn:xmpp:avatar:data'}) | |
96 .c('item', {id: avatar_data.id}) | |
97 .c('data', {xmlns: 'urn:xmpp:avatar:data'}) | |
98 .t(avatar_data.data); | |
99 connection.sendIQ(data_iq, onAvatarDataUpload, onAvatarUploadError); | |
100 }); | |
101 | |
102 function onAvatarMetadataUpload(iq) | |
103 { | |
104 console.log("onAvatarMetadataUpload", iq); | |
105 } | |
106 | |
107 function onAvatarDataUpload(iq) | |
108 { | |
109 console.log('Avatar successfully uploaded!', iq); | |
110 avatar_change.disabled = true; | |
111 avatar_size.innerHTML = ''; | |
112 } | |
113 | |
114 function onAvatarUploadError(iq) | |
115 { | |
116 console.log("onAvatarUploadError", iq); | |
117 } | |
118 | |
119 avatar_file.addEventListener('change', function (evt) { | |
120 const file = evt.target.files[0]; | |
121 avatar_data.type = file.type; | |
122 avatar_data.bytes = file.size; | |
123 | |
124 // Set the preview. | |
125 avatar_img.src = URL.createObjectURL(file); | |
126 const [size, unit] = friendlyDataSize(file.size); | |
127 avatar_size.innerHTML = Math.round(size) + ' ' + unit; | |
128 | |
129 // Obtain the base64 version of this file. | |
130 const base64_reader = new FileReader(); | |
131 base64_reader.onload = function (evt) { | |
132 const data = evt.target.result; | |
133 avatar_data.data = data.substr(data.indexOf(',') + 1); | |
134 } | |
135 base64_reader.readAsDataURL(file); | |
136 | |
137 // Compute the sha1 of this file. | |
138 const sha1_reader = new FileReader(); | |
139 sha1_reader.onload = async function (evt) { | |
140 const data = evt.target.result; | |
141 const digest = await window.crypto.subtle.digest('SHA-1', data); | |
142 const sha1 = (Array | |
143 .from(new Uint8Array(digest)) | |
144 .map(b => b.toString(16).padStart(2, "0")) | |
145 .join("")); | |
146 avatar_data.id = sha1; | |
147 avatar_change.disabled = false; | |
148 } | |
149 sha1_reader.readAsArrayBuffer(file); | |
150 }); | |
151 | |
152 function friendlyDataSize(bytes) { | |
153 let unit = 'B' | |
154 if (bytes >= 1024) { | |
155 bytes /= 1024; | |
156 unit = 'KiB'; | |
157 } | |
158 if (bytes >= 1024) { | |
159 bytes /= 1024; | |
160 unit = 'MiB'; | |
161 } | |
162 if (bytes >= 1024) { | |
163 bytes /= 1024; | |
164 unit = 'GiB'; | |
165 } | |
166 if (bytes >= 1024) { | |
167 bytes /= 1024; | |
168 unit = 'TiB'; | |
169 } | |
170 return [bytes, unit]; | |
171 } | |
2
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
172 |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
173 avatar_access.addEventListener('change', function (evt) { |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
174 const iq = $iq({type: 'set'}) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
175 .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub#owner'}) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
176 .c('configure', {node: 'urn:xmpp:avatar:metadata'}) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
177 .c('x', {xmlns: 'jabber:x:data', type: 'submit'}) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
178 .c('field', {'var': 'FORM_TYPE', type: 'hidden'}) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
179 .c('value') |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
180 .t('http://jabber.org/protocol/pubsub#node_config') |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
181 .up() |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
182 .up() |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
183 .c('field', {'var': 'pubsub#access_model'}) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
184 .c('value') |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
185 .t(evt.target.value) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
186 .up() |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
187 .up() |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
188 connection.sendIQ(iq, onAvatarConfigured, onAvatarConfigureError.bind(null, 'PubSub configuration failed.')); |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
189 }); |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
190 |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
191 function onAvatarConfigured(result_iq) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
192 { |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
193 console.log('Successfully set avatar access model.') |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
194 } |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
195 |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
196 function onAvatarConfigureError(string) |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
197 { |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
198 console.log('Failed to configure avatar node: ' + string); |
db033e5eabcb
Add pubsub#access_model configuration for avatars.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
1
diff
changeset
|
199 } |
0 | 200 } |