diff --git a/Dockerfile b/Dockerfile index cc5af86..d0495e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -148,6 +148,7 @@ COPY --from=build-ffmpeg /usr/lib/libfdk-aac.so.2 /usr/lib/libfdk-aac.so.2 # Add NGINX path, config and static files. ENV PATH "${PATH}:/usr/local/nginx/sbin" ADD nginx.conf /etc/nginx/nginx.conf +ADD streamer_configs / RUN mkdir -p /opt/data && mkdir /www ADD static /www/static diff --git a/docker-compose.yml b/docker-compose.yml index 3c1ea67..794c75f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,4 +8,8 @@ services: - 8080:80 - 8443:443 volumes: - - ./certs:/opt/certs \ No newline at end of file + #- ./certs:/opt/certs + - ./nginx.conf:/etc/nginx/nginx.conf + - ./streamer_configs:/streamer_configs + - ./static:/www/static + - ./hls:/www/hls \ No newline at end of file diff --git a/hls/STREAMER1 copy.m3u8 b/hls/STREAMER1 copy.m3u8 new file mode 100644 index 0000000..4bde688 --- /dev/null +++ b/hls/STREAMER1 copy.m3u8 @@ -0,0 +1,8 @@ +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=5128000,NAME="720p60 (Source)",RESOLUTION=1280x720,FRAME_RATE=60 +STREAMER1/src.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2509600,NAME="480p30",RESOLUTION=854x480,FRAME_RATE=30 +STREAMER1/med.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1048000,NAME="240p30",RESOLUTION=426x240,FRAME_RATE=30 +STREAMER1/low.m3u8 \ No newline at end of file diff --git a/hls/STREAMER1.m3u8 b/hls/STREAMER1.m3u8 new file mode 100644 index 0000000..e918cbb --- /dev/null +++ b/hls/STREAMER1.m3u8 @@ -0,0 +1,4 @@ +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=5128000,NAME="720p60 (Source)",RESOLUTION=1280x720,FRAME_RATE=60 +STREAMER1/src.m3u8 \ No newline at end of file diff --git a/nginx.conf b/nginx.conf index e6072d4..65d7489 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,46 +1,33 @@ daemon off; -error_log /dev/stdout info; +error_log /dev/stderr; events { worker_connections 1024; } rtmp { + access_log /dev/stdout combined; + server { listen 1935; - chunk_size 4000; + live on; - application stream { - live on; - - exec ffmpeg -i rtmp://localhost:1935/stream/$name - -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 2500k -f flv -g 30 -r 30 -s 1280x720 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name_720p2628kbs - -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 1000k -f flv -g 30 -r 30 -s 854x480 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name_480p1128kbs - -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 750k -f flv -g 30 -r 30 -s 640x360 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name_360p878kbs - -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 400k -f flv -g 30 -r 30 -s 426x240 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name_240p528kbs - -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 200k -f flv -g 15 -r 15 -s 426x240 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name_240p264kbs; - } - - application hls { - live on; - hls on; - hls_fragment_naming system; - hls_fragment 5; - hls_playlist_length 10; - hls_path /opt/data/hls; - hls_nested on; - - hls_variant _720p2628kbs BANDWIDTH=2628000,RESOLUTION=1280x720; - hls_variant _480p1128kbs BANDWIDTH=1128000,RESOLUTION=854x480; - hls_variant _360p878kbs BANDWIDTH=878000,RESOLUTION=640x360; - hls_variant _240p528kbs BANDWIDTH=528000,RESOLUTION=426x240; - hls_variant _240p264kbs BANDWIDTH=264000,RESOLUTION=426x240; - } + include /streamer_configs/*.conf; } } http { + #include mime.types; # missing in my container for some reason. + types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + } + access_log /dev/stdout combined; ssl_ciphers HIGH:!aNULL:!MD5; @@ -49,6 +36,7 @@ http { ssl_session_timeout 10m; server { + root /www; listen 80; # Uncomment these lines to enable SSL. @@ -57,24 +45,18 @@ http { # ssl_certificate /opt/certs/example.com.crt; # ssl_certificate_key /opt/certs/example.com.key; + location /hls { + root /www; types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } - root /opt/data; add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; - } - - location /live { - alias /opt/data/hls; - types { - application/vnd.apple.mpegurl m3u8; - video/mp2t ts; - } - add_header Cache-Control no-cache; - add_header Access-Control-Allow-Origin *; + if ($request_method = OPTIONS) { + return 200; + } } location /stat { @@ -82,8 +64,8 @@ http { rtmp_stat_stylesheet static/stat.xsl; } - location /static { - alias /www/static; + location / { + root /www/static; } location = /crossdomain.xml { @@ -91,5 +73,42 @@ http { default_type text/xml; expires 24h; } + + # begin XMPP + + # location /xmpp/bosh { + # proxy_pass http://xmpp:24714/http-bind; + # proxy_set_header Host $host; + # proxy_set_header X-Forwarded-For $remote_addr; + # proxy_buffering off; + # tcp_nodelay on; + # } + # location /xmpp/websocket { + # proxy_pass http://xmpp:24714/xmpp-websocket; + # proxy_http_version 1.1; + # proxy_set_header Connection "Upgrade"; + # proxy_set_header Upgrade $http_upgrade; + # proxy_read_timeout 900s; + # proxy_set_header Host $host; + # proxy_set_header X-Forwarded-For $remote_addr; + # proxy_buffering off; + # tcp_nodelay on; + # } + + # end XMPP + + # streamers + location ~ ^/(STREAMER1|STREAMER2|STREAMER3)$ { + default_type text/html; + alias /www/static/both.html; + } + location ~ ^/(STREAMER1|STREAMER2|STREAMER3)/chat$ { + default_type text/html; + alias /www/static/chat.html; + } + location ~ ^/(STREAMER1|STREAMER2|STREAMER3)/stream$ { + default_type text/html; + alias /www/static/stream.html; + } } } diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..a3697fc --- /dev/null +++ b/notes.md @@ -0,0 +1,11 @@ +to play in vlc +http://192.168.1.69:8080/hls/STREAMER1.m3u8 + +to stream from obs +rtmp://192.168.1.69:1935/live_STREAMKEY + +todo +* latency is high +* no xmpp yet +* configuring streamers is a huge pain +* medium and low transcodes are not working diff --git a/static/STREAMER1.js b/static/STREAMER1.js new file mode 100644 index 0000000..0f15827 --- /dev/null +++ b/static/STREAMER1.js @@ -0,0 +1 @@ +video.poster = "/STREAMER1.jpg"; \ No newline at end of file diff --git a/static/app.css b/static/app.css new file mode 100644 index 0000000..7833065 --- /dev/null +++ b/static/app.css @@ -0,0 +1,126 @@ +*, *:before, *:after { + box-sizing: border-box; + font-family: sans-serif !important; +} +video { + object-fit: cover; + width: 100%; + height: 100%; +} +body { + background: #1A2327; + color: #FFF; + margin: 0; + padding: 0; + height: 100vh; + width: 100vw; + position: fixed; +} +.shaka-video-container { + width: calc(100vw - 300px); + height: calc((100vw - 300px) * (9 / 16)); + position: relative; + top: calc((100vh - ((100vw - 300px) * (9 / 16))) / 2); +} +body.stream-only .shaka-video-container { + width: 100vw; + height: calc((100vw) * (9 / 16)); + position: relative; + top: calc((100vh - ((100vw) * (9 / 16))) / 2); +} +#conversejs.chat { + position: absolute; + right: 0px; + top: 0px; + left: initial; + bottom: initial; + width: 300px; + height: 100vh; + overflow: hidden; + --chatroom-head-color: #009688; + --chatbox-border-radius: 0; +} +#conversejs .col-md-9 { + flex: 0 0 100%; + max-width: 100% !important; +} +#conversejs .message.chat-msg.chat-msg--followup .chat-msg__content { + margin-left: .5rem; +} +.chatroom-body, .chat-content, .chat-toolbar, .chat-textarea, .chatroom-form-container, .converse-form, .form-control, .separator-text { + background-color: #1A2327 !important; + color: #ECEFF1 !important; +} +.btn-primary { + background: #009688 !important; + color: #FFF !important; + opacity: 0.7; + transition: opacity 0.4s !important; +} +.btn-primary:hover { + opacity: 1; +} +.chat-msg__text { + color: inherit !important; + opacity: 0.8; +} +.chat-msg__time { + color: inherit !important; + opacity: 0.5; +} +.far:before { + font-family: ConverseFontAwesomeRegular !important; +} +.fa:before, .fas:before { + font-family: ConverseFontAwesomeSolid !important; +} +.chat-msg.correcting { + background: #F44336 !important; +} +.material-icons { + font-family: 'Material Icons' !important; +} +.chat-msg { + padding: .125rem .5rem !important; +} +.toggle-occupants, .occupants, .chat-head, .avatar, .chat-msg__receipt, .chat-state-notification { + display: none !important; +} +body.tall .shaka-video-container { + width: calc(100vw); + height: calc(100vw * (9 / 16)); + top: 0; +} +body.stream-only.tall .shaka-video-container { + width: calc(100vw); + height: calc(100vw * (9 / 16)); + top: calc((100vh - (100vw * (9 / 16))) / 2); +} +body.tall #conversejs.chat { + position: relative; + width: 100vw; + height: calc(100vh - (100vw * (9 / 16))); +} +body.wide .shaka-video-container { + width: calc(100vh * (16 / 9)); + height: 100vh; + position: relative; + top: 0; + left: calc(((100vw - 300px) - (100vh * (16 / 9))) / 2); +} +body.stream-only.wide .shaka-video-container { + width: calc(100vh * (16 / 9)); + height: 100vh; + position: relative; + top: 0; + left: calc(((100vw) - (100vh * (16 / 9))) / 2); +} +.popout-chat { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + width: 100vw !important; + height: 100vh !important; +} diff --git a/static/both.html b/static/both.html new file mode 100644 index 0000000..0c7402e --- /dev/null +++ b/static/both.html @@ -0,0 +1,33 @@ + + +
+ + +'+n(e.__("Hold tight, we're fetching the registration form…"))+"
\n",e.cancel&&(t+='\n \n"),t+="\n"}},function(e,t,n){var o={escape:n(1)};e.exports=function(e){var t="",n=o.escape;Array.prototype.join;return t+='\x3c!-- src/templates/registration_form.html --\x3e\n\n'+n(e.title)+'
\n'+n(e.instructions)+'
\n\n\n\n"}},function(e,t,n){var o={escape:n(1)};e.exports=function(e){var t="",n=o.escape;Array.prototype.join;return t+='\x3c!-- src/templates/register_panel.html --\x3e\n'+n(e.label_jid)+" "+n(e.jid)+'
\n'+n(e.label_desc)+" "+n(e.desc)+'
\n'+n(e.label_occ)+" "+n(e.occ)+'
\n'+n(e.label_features)+"\n
'+(0,o.escape)(e.label_occupants)+'
\n'+(null==(t=e.description)?"":t)+'
\n'+n(e.__("Features"))+'
\n'+n(e)+"
\n "}),t+="\n'+n(e.reason)+"
\n\n ",e.jid&&(t+='\n\n '+n(e.__("The conversation has moved. Click below to enter."))+'\n
\n \n "),t+="\n'+(0,o.escape)(e.version_name)+'
\nOpen Source XMPP chat client brought to you by Opkode
\nTranslate it into your own language
\n'+n(e.message)+"
'+i.a.get(e.querySelector("value"),"textContent")+"
"}return"jid-multi"===e.getAttribute("type")?fn()({name:e.getAttribute("var"),label:e.getAttribute("label")||"",value:i.a.get(e.querySelector("value"),"textContent"),required:!i.a.isNil(e.querySelector("required"))}):"boolean"===e.getAttribute("type")?nn()({id:C.getUniqueId(),name:e.getAttribute("var"),label:e.getAttribute("label")||"",checked:"1"===i.a.get(e.querySelector("value"),"textContent")?'checked="1"':"",required:!i.a.isNil(e.querySelector("required"))}):"url"===e.getAttribute("var")?_n()({label:e.getAttribute("label")||"",value:i.a.get(e.querySelector("value"),"textContent")}):"username"===e.getAttribute("var")?ln()({domain:" @"+n,name:e.getAttribute("var"),type:kn[e.getAttribute("type")],label:e.getAttribute("label")||"",value:i.a.get(e.querySelector("value"),"textContent"),required:!i.a.isNil(e.querySelector("required"))}):an()({id:C.getUniqueId(),label:e.getAttribute("label")||"",name:e.getAttribute("var"),placeholder:null,required:!i.a.isNil(e.querySelector("required")),type:kn[e.getAttribute("type")],value:i.a.get(e.querySelector("value"),"textContent")})}if("ocr"===e.getAttribute("var")){const n=e.querySelector("uri"),o=M()('data[cid="'+n.textContent.replace(/^cid:/,"")+'"]',t)[0];return en()({label:e.getAttribute("label"),name:e.getAttribute("var"),data:i.a.get(o,"textContent"),type:n.getAttribute("type"),required:!i.a.isNil(e.querySelector("required"))})}};var Sn=n(97),Mn=n.n(Sn),An=n(96),Cn=n.n(An),En=n(5),Tn=n.n(En),Ln=n(95),jn=n.n(Ln),Nn=n(94),On=n.n(Nn),Dn=n(10),Rn=n.n(Dn);const In=P.env,Pn=(In.Backbone,In._),Hn=In.moment;P.plugins.add("converse-message-view",{dependencies:["converse-modal","converse-chatboxviews"],initialize(){const e=this._converse,t=e.__;e.api.settings.update({show_images_inline:!0}),e.MessageVersionsModal=e.BootstrapModal.extend({toHTML(){return On()(Pn.extend(this.model.toJSON(),{__:t}))}}),e.MessageView=e.ViewWithAvatar.extend({events:{"click .chat-msg__edit-modal":"showMessageVersionsModal"},initialize(){this.model.vcard&&this.model.vcard.on("change",this.render,this),this.model.on("change",this.onChanged,this),this.model.on("destroy",this.remove,this)},async render(){const t=je.hasClass("chat-msg--followup",this.el);if(this.model.isOnlyChatStateNotification())this.renderChatStateNotification();else if(this.model.get("file")&&!this.model.get("oob_url")){if(!this.model.file)return e.log("Attempted to render a file upload message with no file data"),this.el;this.renderFileUploadProgresBar()}else"error"===this.model.get("type")?this.renderErrorMessage():await this.renderChatMessage();return t&&je.addClass("chat-msg--followup",this.el),this.el},async onChanged(e){const t=e.changed.edited;if(this.model.changed.progress)return this.renderFileUploadProgresBar();Pn.filter(["correcting","message","type","upload","received"],e=>Object.prototype.hasOwnProperty.call(this.model.changed,e)).length&&await this.render(),t&&this.onMessageEdited()},onMessageEdited(){this.model.get("is_archived")||(this.el.addEventListener("animationend",()=>je.removeClass("onload",this.el)),je.addClass("onload",this.el))},replaceElement(e){return Pn.isNil(this.el.parentElement)||this.el.parentElement.replaceChild(e,this.el),this.setElement(e),this.el},async renderChatMessage(){const n=this.isMeCommand(),o=Hn(this.model.get("time")),a=this.model.vcard?this.model.vcard.get("role"):null,r=a?a.split(","):[],s=je.stringToElement(jn()(Pn.extend(this.model.toJSON(),{__:t,is_me_message:n,roles:r,pretty_time:o.format(e.time_format),time:o.format(),extra_classes:this.getExtraMessageClasses(),label_show:t("Show more"),username:this.model.getDisplayName()}))),c=this.model.get("oob_url");c&&(s.querySelector(".chat-msg__media").innerHTML=Pn.flow(Pn.partial(je.renderFileURL,e),Pn.partial(je.renderMovieURL,e),Pn.partial(je.renderAudioURL,e),Pn.partial(je.renderImageURL,e))(c));let f=this.getMessageText();const i=s.querySelector(".chat-msg__text");f&&f!==c&&(n&&(f=f.substring(4)),f=Rn.a.filterXSS(f,{whiteList:{}}),i.innerHTML=Pn.flow(Pn.partial(je.geoUriToHttp,Pn,e.geouri_replacement),Pn.partial(je.addMentionsMarkup,Pn,this.model.get("references"),this.model.collection.chatbox),je.addHyperlinks,je.renderNewLines,Pn.partial(je.addEmoji,e,Pn))(f));const u=je.renderImageURLs(e,i);"headline"!==this.model.get("type")&&this.renderAvatar(s),await u,this.replaceElement(s),this.model.collection.trigger("rendered",this)},renderErrorMessage(){const e=Hn(this.model.get("time")),t=je.stringToElement(Tn()(Pn.extend(this.model.toJSON(),{extra_classes:"chat-error",isodate:e.format()})));return this.replaceElement(t)},renderChatStateNotification(){let n;const o=this.model.get("from"),a=this.model.getDisplayName();if(this.model.get("chat_state")===e.COMPOSING)n="me"===this.model.get("sender")?t("Typing from another device"):t("%1$s is typing",a);else if(this.model.get("chat_state")===e.PAUSED)n="me"===this.model.get("sender")?t("Stopped typing on the other device"):t("%1$s has stopped typing",a);else{if(this.model.get("chat_state")!==e.GONE)return;n=t("%1$s has gone away",a)}const r=Hn().format();this.replaceElement(je.stringToElement(Mn()({message:n,from:o,isodate:r})))},renderFileUploadProgresBar(){const e=je.stringToElement(Cn()(Pn.extend(this.model.toJSON(),{__:t,filename:this.model.file.name,filesize:St()(this.model.file.size)})));this.replaceElement(e),this.renderAvatar()},showMessageVersionsModal(t){t.preventDefault(),Pn.isUndefined(this.model.message_versions_modal)&&(this.model.message_versions_modal=new e.MessageVersionsModal({model:this.model})),this.model.message_versions_modal.show(t)},getMessageText(){return this.model.get("is_encrypted")?this.model.get("plaintext")||(e.debug?t("Unencryptable OMEMO message"):null):this.model.get("message")},isMeCommand(){const e=this.getMessageText();return!!e&&e.startsWith("/me ")},processMessageText(){var t=this.get("message");t=je.geoUriToHttp(t,e.geouri_replacement)},getExtraMessageClasses(){let e=this.model.get("is_delayed")?"delayed":"";return"groupchat"===this.model.get("type")&&"them"===this.model.get("sender")&&this.model.collection.chatbox.isUserMentioned(this.model)&&(e+=" mentioned"),this.model.get("correcting")&&(e+=" correcting"),e}})}});var zn=n(9),Fn=n.n(zn),Bn=n(93),qn=n.n(Bn);const Yn=P.env,Un=Yn.Strophe,$n=Yn.Backbone,Wn=Yn._;P.plugins.add("converse-modal",{initialize(){const e=this._converse;let t;e.BootstrapModal=$n.VDOMView.extend({initialize(){this.render().insertIntoDOM(),this.modal=new Fn.a.Modal(this.el,{backdrop:"static",keyboard:!0}),this.el.addEventListener("hide.bs.modal",e=>{Wn.isNil(this.trigger_el)||this.trigger_el.classList.remove("selected")},!1)},insertIntoDOM(){e.chatboxviews.el.querySelector("#converse-modals").insertAdjacentElement("beforeEnd",this.el)},show(e){e&&(e.preventDefault(),this.trigger_el=e.target,this.trigger_el.classList.add("selected")),this.modal.show()}}),e.Alert=e.BootstrapModal.extend({initialize(){e.BootstrapModal.prototype.initialize.apply(this,arguments),this.model.on("change",this.render,this)},toHTML(){return qn()(this.model.toJSON())}}),e.api.listen.on("afterTearDown",()=>{if(!e.chatboxviews)return;const t=e.chatboxviews.el.querySelector("#converse-modals");t&&(t.innerHTML="")}),Wn.extend(e.api,{alert:{show(n,o,a){if(Wn.isString(a)&&(a=[a]),n===Un.LogLevel.ERROR?n="alert-danger":n===Un.LogLevel.INFO?n="alert-info":n===Un.LogLevel.WARN&&(n="alert-warning"),Wn.isUndefined(t)){const r=new $n.Model({title:o,messages:a,type:n});t=new e.Alert({model:r})}else t.model.set({title:o,messages:a,type:n});t.show()}}})}});var Vn=n(92),Jn=n.n(Vn),Gn=n(18),Qn=n.n(Gn),Xn=n(91),Kn=n.n(Xn),Zn=n(90),eo=n.n(Zn),to=n(89),no=n.n(to),oo=n(88),ao=n.n(oo),ro=n(87),so=n.n(ro),co=n(86),fo=n.n(co),io=n(8),uo=n.n(io),_o=n(85),lo=n.n(_o),ho=n(84),mo=n.n(ho),go=n(83),po=n.n(go),yo=n(82),bo=n.n(yo),vo=n(81),wo=n.n(vo);const ko=P.env,xo=(ko.$msg,ko.Backbone),So=ko.Promise,Mo=ko.Strophe,Ao=ko._,Co=(ko.b64_sha1,ko.f),Eo=ko.sizzle,To=ko.moment;P.plugins.add("converse-chatview",{dependencies:["converse-chatboxviews","converse-disco","converse-message-view","converse-modal"],initialize(){const e=this._converse,t=e.__;e.api.settings.update({emoji_image_path:ke.base,show_send_button:!1,show_toolbar:!0,time_format:"HH:mm",use_system_emojis:!0,visible_toolbar_buttons:{call:!1,clear:!0,emoji:!0,spoiler:!0}}),ke.base=e.emoji_image_path,e.api.listen.on("windowStateChanged",function(t){e.chatboxviews&&e.chatboxviews.each(e=>{"controlbox"!==e.model.get("id")&&e.onWindowStateChanged(t.state)})}),e.EmojiPicker=xo.Model.extend({defaults:{current_category:"people",current_skintone:"",scroll_position:0}}),e.EmojiPickerView=xo.VDOMView.extend({className:"emoji-picker-container",events:{"click .emoji-category-picker li.emoji-category":"chooseCategory","click .emoji-skintone-picker li.emoji-skintone":"chooseSkinTone"},initialize(){this.model.on("change:current_skintone",this.render,this),this.model.on("change:current_category",this.render,this)},toHTML(){return no()(Ao.extend(this.model.toJSON(),{_:Ao,transform:je.getEmojiRenderer(e),emojis_by_category:je.getEmojisByCategory(e),toned_emojis:je.getTonedEmojis(e),skintones:["tone1","tone2","tone3","tone4","tone5"],shouldBeHidden:this.shouldBeHidden}))},shouldBeHidden(e,t,n){if(Ao.includes(e,"_tone")){if(!t||!Ao.includes(e,t))return!0}else if(t&&Ao.includes(n,e))return!0;return!1},chooseSkinTone(e){e.preventDefault(),e.stopPropagation();const t=("IMG"===e.target.nodeName?e.target.parentElement:e.target).getAttribute("data-skintone").trim();this.model.get("current_skintone")===t?this.model.save({current_skintone:""}):this.model.save({current_skintone:t})},chooseCategory(e){e.preventDefault(),e.stopPropagation();const t=("IMG"===e.target.nodeName?e.target.parentElement:e.target).getAttribute("data-category").trim();this.model.save({current_category:t,scroll_position:0})}}),e.ChatBoxHeading=e.ViewWithAvatar.extend({initialize(){this.model.on("change:status",this.onStatusMessageChanged,this),this.model.vcard.on("change",this.render,this)},render(){return this.el.innerHTML=Kn()(Ao.extend(this.model.vcard.toJSON(),this.model.toJSON(),{_converse:e,info_close:t("Close this chat box")})),this.renderAvatar(),this},onStatusMessageChanged(t){this.render(),e.emit("contactStatusMessageChanged",{contact:t.attributes,message:t.get("status")})}}),e.UserDetailsModal=e.BootstrapModal.extend({events:{"click button.remove-contact":"removeContact","click button.refresh-contact":"refreshContact","click .fingerprint-trust .btn input":"toggleDeviceTrust"},initialize(){e.BootstrapModal.prototype.initialize.apply(this,arguments),this.model.on("contactAdded",this.registerContactEventHandlers,this),this.model.on("change",this.render,this),this.registerContactEventHandlers(),e.emit("userDetailsModalInitialized",this.model)},toHTML(){return wo()(Ao.extend(this.model.toJSON(),this.model.vcard.toJSON(),{_:Ao,__:t,view:this,_converse:e,allow_contact_removal:e.allow_contact_removal,display_name:this.model.getDisplayName(),is_roster_contact:!Ao.isUndefined(this.model.contact),utils:je}))},registerContactEventHandlers(){Ao.isUndefined(this.model.contact)||(this.model.contact.on("change",this.render,this),this.model.contact.vcard.on("change",this.render,this),this.model.contact.on("destroy",()=>{delete this.model.contact,this.render()}))},async refreshContact(n){n&&n.preventDefault&&n.preventDefault();const o=this.el.querySelector(".fa-refresh");je.addClass("fa-spin",o);try{await e.api.vcard.update(this.model.contact.vcard,!0)}catch(n){e.log(n,Mo.LogLevel.FATAL),this.el.querySelector(".modal-body").insertAdjacentHTML("afterBegin",Jn()({type:"alert-danger",message:t("Sorry, something went wrong while trying to refresh")}))}je.removeClass("fa-spin",o)},removeContact(n){if(n&&n.preventDefault&&n.preventDefault(),!e.allow_contact_removal)return;!0===confirm(t("Are you sure you want to remove this contact?"))&&(this.modal.hide(),this.model.contact.removeFromRoster(e=>{this.model.contact.destroy()},n=>{e.log(n,Mo.LogLevel.ERROR),e.api.alert.show(Mo.LogLevel.ERROR,t("Error"),[t("Sorry, there was an error while trying to remove %1$s as a contact.",this.model.contact.getDisplayName())])}))}}),e.ChatBoxView=xo.NativeView.extend({length:200,className:"chatbox hidden",is_chatroom:!1,events:{"change input.fileupload":"onFileSelection","click .chat-msg__action-edit":"onMessageEditButtonClicked","click .chatbox-navback":"showControlBox","click .close-chatbox-button":"close","click .new-msgs-indicator":"viewUnreadMessages","click .send-button":"onFormSubmitted","click .show-user-details-modal":"showUserDetailsModal","click .spoiler-toggle":"toggleSpoilerMessage","click .toggle-call":"toggleCall","click .toggle-clear":"clearMessages","click .toggle-compose-spoiler":"toggleComposeSpoilerMessage","click .toggle-smiley ul.emoji-picker li":"insertEmoji","click .toggle-smiley":"toggleEmojiMenu","click .upload-file":"toggleFileUpload","input .chat-textarea":"inputChanged","keydown .chat-textarea":"keyPressed","dragover .chat-textarea":"onDragOver","drop .chat-textarea":"onDrop"},initialize(){this.initDebounced(),this.model.messages.on("add",this.onMessageAdded,this),this.model.messages.on("rendered",this.scrollDown,this),this.model.on("show",this.show,this),this.model.on("destroy",this.remove,this),this.model.presence.on("change:show",this.onPresenceChanged,this),this.model.on("showHelpMessages",this.showHelpMessages,this),this.render(),this.fetchMessages(),e.emit("chatBoxOpened",this),e.emit("chatBoxInitialized",this)},initDebounced(){this.scrollDown=Ao.debounce(this._scrollDown,250),this.markScrolled=Ao.debounce(this._markScrolled,100),this.show=Ao.debounce(this._show,250,{leading:!0})},render(){return this.el.setAttribute("id",this.model.get("box_id")),this.el.innerHTML=Qn()(Ao.extend(this.model.toJSON(),{unread_msgs:t("You have unread messages")})),this.content=this.el.querySelector(".chat-content"),this.renderMessageForm(),this.insertHeading(),this},renderToolbar(t,n){return e.show_toolbar?(t=t||po.a,n=Ao.assign(this.model.toJSON(),this.getToolbarOptions(n||{})),this.el.querySelector(".chat-toolbar").innerHTML=t(n),this.addSpoilerButton(n),this.addFileUploadButton(),e.emit("renderToolbar",this),this):this},renderMessageForm(){let n;n=this.model.get("composing_spoiler")?t("Hidden message"):t("Message"),this.el.querySelector(".message-form-container").innerHTML=eo()(Ao.extend(this.model.toJSON(),{hint_value:Ao.get(this.el.querySelector(".spoiler-hint"),"value"),label_message:n,label_send:t("Send"),label_spoiler_hint:t("Optional hint"),message_value:Ao.get(this.el.querySelector(".chat-textarea"),"value"),show_send_button:e.show_send_button,show_toolbar:e.show_toolbar,unread_msgs:t("You have unread messages")})),this.renderToolbar()},showControlBox(){e.chatboxviews.get("controlbox").show(),this.hide()},showUserDetailsModal(t){t.preventDefault(),Ao.isUndefined(this.user_details_modal)&&(this.user_details_modal=new e.UserDetailsModal({model:this.model})),this.user_details_modal.show(t)},toggleFileUpload(e){this.el.querySelector("input.fileupload").click()},onFileSelection(e){this.model.sendFiles(e.target.files)},onDragOver(e){e.preventDefault()},onDrop(e){0!=e.dataTransfer.files.length&&(e.preventDefault(),this.model.sendFiles(e.dataTransfer.files))},async addFileUploadButton(n){(await e.api.disco.supports(Mo.NS.HTTPUPLOAD,e.domain)).length&&this.el.querySelector(".chat-toolbar").insertAdjacentHTML("beforeend",bo()({tooltip_upload_file:t("Choose a file to send")}))},async addSpoilerButton(t){if(!t.show_spoiler_button||this.model.get("type")===e.CHATROOMS_TYPE)return;const n=this.model.get("jid");if(0===this.model.presence.resources.length)return;const o=await So.all(this.model.presence.resources.map(t=>e.api.disco.supports(Mo.NS.SPOILER,`${n}/${t.get("name")}`)));if(Ao.filter(o,"length").length){const t=lo()(this.model.toJSON());e.visible_toolbar_buttons.emoji?this.el.querySelector(".toggle-smiley").insertAdjacentHTML("afterEnd",t):this.el.querySelector(".chat-toolbar").insertAdjacentHTML("afterBegin",t)}},insertHeading(){this.heading=new e.ChatBoxHeading({model:this.model}),this.heading.render(),this.heading.chatview=this,Ao.isUndefined(this.model.contact)||this.model.contact.on("destroy",this.heading.render,this);const t=this.el.querySelector(".flyout");return t.insertBefore(this.heading.el,t.querySelector(".chat-body")),this},getToolbarOptions(n){let o;return o=this.model.get("composing_spoiler")?t("Click to write as a normal (non-spoiler) message"):t("Click to write your message as a spoiler"),Ao.extend(n||{},{label_clear:t("Clear all messages"),tooltip_insert_smiley:t("Insert emojis"),tooltip_start_call:t("Start a call"),label_toggle_spoiler:o,show_call_button:e.visible_toolbar_buttons.call,show_spoiler_button:e.visible_toolbar_buttons.spoiler,use_emoji:e.visible_toolbar_buttons.emoji})},afterMessagesFetched(){this.insertIntoDOM(),this.scrollDown(),this.content.addEventListener("scroll",this.markScrolled.bind(this)),e.emit("afterMessagesFetched",this)},fetchMessages(){return this.model.messages.fetch({add:!0,success:this.afterMessagesFetched.bind(this),error:this.afterMessagesFetched.bind(this)}),this},insertIntoDOM(){return e.chatboxviews.insertRowColumn(this.el),this},showChatEvent(e){const t=To().format();return this.content.insertAdjacentHTML("beforeend",Tn()({extra_classes:"chat-event",message:e,isodate:t})),this.insertDayIndicator(this.content.lastElementChild),this.scrollDown(),t},showErrorMessage(e){this.content.insertAdjacentHTML("beforeend",ao()({message:e,isodate:To().format()})),this.scrollDown()},addSpinner(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];Ao.isNull(this.el.querySelector(".spinner"))&&(e?(this.content.insertAdjacentHTML("beforeend",uo()()),this.scrollDown()):this.content.insertAdjacentHTML("afterbegin",uo()()))},clearSpinner(){Ao.each(this.content.querySelectorAll("span.spinner"),e=>e.parentNode.removeChild(e))},insertDayIndicator(e){const t=je.getPreviousElement(e,".message:not(.chat-state-notification)"),n=Ao.isNull(t)?null:t.getAttribute("data-isodate"),o=e.getAttribute("data-isodate");if((!Ao.isNull(n)||!Ao.isNull(o))&&(Ao.isNull(n)||To(o).isAfter(n,"day"))){const t=To(o).startOf("day");e.insertAdjacentHTML("beforeBegin",fo()({isodate:t.format(),datestring:t.format("dddd MMM Do YYYY")}))}},getLastMessageDate(e){const t=je.getFirstChildElement(this.content,".message:not(.chat-state-notification)"),n=t?t.getAttribute("data-isodate"):null;if(!Ao.isNull(n)&&To(n).isAfter(e))return null;const o=je.getLastChildElement(this.content,".message:not(.chat-state-notification)"),a=o?o.getAttribute("data-isodate"):null;if(Ao.isNull(a)||To(a).isBefore(e))return a;const r=Ao.invokeMap(Eo(".message:not(.chat-state-notification)",this.content),Element.prototype.getAttribute,"data-isodate");Ao.isObject(e)&&(e=e.format()),r.push(e),r.sort();const s=r.lastIndexOf(e);return 0===s?null:r[s-1]},setScrollPosition(e){if(this.model.get("scrolled")){const t=je.getNextElement(e,".chat-msg");if(t&&(0===this.content.scrollTop||this.model.get("top_visible_message"))){const e=this.model.get("top_visible_message")||t;this.model.set("top_visible_message",e),this.content.scrollTop=e.offsetTop-30}}else this.scrollDown()},showHelpMessages(e,t,n){return Ao.each(e,e=>{this.content.insertAdjacentHTML("beforeend",so()({isodate:To().format(),type:t,message:Rn.a.filterXSS(e,{whiteList:{strong:[]}})}))}),!0===n?this.addSpinner():!1===n&&this.clearSpinner(),this.scrollDown()},clearChatStateNotification(e,t){t?Ao.each(Eo(`.chat-state-notification[data-csn="${e.get("from")}"][data-isodate="${t}"]`,this.content),je.removeElement):Ao.each(Eo(`.chat-state-notification[data-csn="${e.get("from")}"]`,this.content),je.removeElement)},shouldShowOnTextMessage(){return!je.isVisible(this.el)},insertMessage(e){if("error"===e.model.get("type")){const t=this.content.querySelector(`[data-msgid="${e.model.get("msgid")}"]`);if(t)return t.insertAdjacentElement("afterend",e.el),this.trigger("messageInserted",e.el)}const t=To(e.model.get("time"))||To,n=this.getLastMessageDate(t);if(Ao.isNull(n))this.content.insertAdjacentElement("afterbegin",e.el);else{const t=Eo(`[data-isodate="${n}"]:last`,this.content).pop();if("error"===e.model.get("type")&&je.hasClass("chat-error",t)&&t.textContent===e.model.get("message"))return;t.insertAdjacentElement("afterend",e.el),this.markFollowups(e.el)}return this.trigger("messageInserted",e.el)},markFollowups(e){const t=e.getAttribute("data-from"),n=e.previousElementSibling,o=To(e.getAttribute("data-isodate")),a=e.nextElementSibling;je.hasClass("chat-msg--action",e)||je.hasClass("chat-msg--action",n)||n.getAttribute("data-from")!==t||!o.isBefore(To(n.getAttribute("data-isodate")).add(10,"minutes"))||e.getAttribute("data-encrypted")!==n.getAttribute("data-encrypted")||je.addClass("chat-msg--followup",e),a&&(!je.hasClass("chat-msg--action","el")&&a.getAttribute("data-from")===t&&To(a.getAttribute("data-isodate")).isBefore(o.add(10,"minutes"))&&e.getAttribute("data-encrypted")===a.getAttribute("data-encrypted")?je.addClass("chat-msg--followup",a):je.removeClass("chat-msg--followup",a))},async showMessage(t){if(!je.isNewMessage(t)&&je.isEmptyMessage(t))return t.destroy();const n=new e.MessageView({model:t});await n.render(),this.clearChatStateNotification(t),this.insertMessage(n),this.insertDayIndicator(n.el),this.setScrollPosition(n.el),je.isNewMessage(t)&&("me"===t.get("sender")?this.model.set("scrolled",!1):this.model.get("scrolled",!0)&&!je.isOnlyChatStateNotification(t)&&this.showNewMessagesIndicator()),this.shouldShowOnTextMessage()?this.show():this.scrollDown()},onMessageAdded(t){this.showMessage(t),t.get("correcting")&&this.insertIntoTextArea(t.get("message"),!0,!0),e.emit("messageAdded",{message:t,chatbox:this.model})},parseMessageForCommands(e){const n=e.replace(/^\s*/,"").match(/^\/(.*)\s*$/);if(n){if("clear"===n[1])return this.clearMessages(),!0;if("help"===n[1]){const e=[`/clear: ${t("Remove messages")}`,`/me: ${t("Write in the third person")}`,`/help: ${t("Show this menu")}`];return this.showHelpMessages(e),!0}}},setChatState(t,n){return Ao.isUndefined(this.chat_state_timeout)||(window.clearTimeout(this.chat_state_timeout),delete this.chat_state_timeout),t===e.COMPOSING?this.chat_state_timeout=window.setTimeout(this.setChatState.bind(this),e.TIMEOUTS.PAUSED,e.PAUSED):t===e.PAUSED&&(this.chat_state_timeout=window.setTimeout(this.setChatState.bind(this),e.TIMEOUTS.INACTIVE,e.INACTIVE)),this.model.set("chat_state",t,n),this},async onFormSubmitted(t){t.preventDefault();const n=this.el.querySelector(".chat-textarea"),o=n.value;if(!o.replace(/\s/g,"").length)return;if(!e.connection.authenticated)return void this.showHelpMessages(["Sorry, the connection has been lost, and your message could not be sent"],"error");let a,r={};this.model.get("composing_spoiler")&&(a=(r=this.el.querySelector("form.sendXMPPMessage input.spoiler-hint")).value),je.addClass("disabled",n),n.setAttribute("disabled","disabled"),(this.parseMessageForCommands(o)||await this.model.sendMessage(this.model.getOutgoingMessageAttributes(o,a)))&&(r.value="",n.value="",je.removeClass("correcting",n),n.style.height="auto",e.emit("messageSend",o)),n.removeAttribute("disabled"),je.removeClass("disabled",n),n.focus(),this.setChatState(e.ACTIVE,{silent:!0})},keyPressed(t){if(!t.ctrlKey){if(!t.shiftKey&&!t.altKey){if(t.keyCode===e.keycodes.FORWARD_SLASH)return;if(t.keyCode===e.keycodes.ESCAPE)return this.onEscapePressed(t);if(t.keyCode===e.keycodes.ENTER)return this.emoji_dropdown&&je.isVisible(this.emoji_dropdown.el.querySelector(".emoji-picker"))&&this.emoji_dropdown.toggle(),this.onFormSubmitted(t);if(t.keyCode===e.keycodes.UP_ARROW&&!t.target.selectionEnd)return this.editEarlierMessage();if(t.keyCode===e.keycodes.DOWN_ARROW&&t.target.selectionEnd===t.target.value.length)return this.editLaterMessage()}Ao.includes([e.keycodes.SHIFT,e.keycodes.META,e.keycodes.META_RIGHT,e.keycodes.ESCAPE,e.keycodes.ALT],t.keyCode)||this.model.get("chat_state")!==e.COMPOSING&&this.setChatState(e.COMPOSING)}},getOwnMessages(){return Co(this.model.messages.filter({sender:"me"}))},onEscapePressed(e){e.preventDefault();const t=this.model.messages.findLastIndex("correcting"),n=t>=0?this.model.messages.at(t):null;n&&n.save("correcting",!1),this.insertIntoTextArea("",!0,!1)},onMessageEditButtonClicked(e){e.preventDefault();const t=this.model.messages.findLastIndex("correcting"),n=t>=0?this.model.messages.at(t):null,o=je.ancestor(e.target,".chat-msg"),a=this.model.messages.findWhere({msgid:o.getAttribute("data-msgid")});n!==a?(Ao.isNil(n)||n.save("correcting",!1),a.save("correcting",!0),this.insertIntoTextArea(je.prefixMentions(a),!0,!0)):(a.save("correcting",!1),this.insertIntoTextArea("",!0,!1))},editLaterMessage(){let e,t=this.model.messages.findLastIndex("correcting");if(t>=0)for(this.model.messages.at(t).save("correcting",!1);t${f}
`),rs.each(s,t=>{(0===e.roomconfig_whitelist.length||rs.includes(e.roomconfig_whitelist,t.getAttribute("var")))&&r.insertAdjacentHTML("beforeend",us.xForm2webForm(t,n))});const i=document.createElement("fieldset");i.insertAdjacentHTML("beforeend",``),i.insertAdjacentHTML("beforeend",``),a.insertAdjacentElement("beforeend",i),i.querySelector("input[type=button]").addEventListener("click",e=>{e.preventDefault(),this.closeForm()}),a.addEventListener("submit",e=>{e.preventDefault(),this.model.saveConfiguration(e.target).then(()=>this.model.refreshRoomFeatures()),this.closeForm()},!1)},closeForm(){us.removeElement(this.el.querySelector(".chatroom-form-container")),this.renderAfterTransition()},getAndRenderConfigurationForm(t){this.showSpinner(),this.model.fetchRoomConfiguration().then(e=>this.renderConfigurationForm(e)).catch(rs.partial(e.log,rs,ns.LogLevel.ERROR))},submitNickname(e){e.preventDefault();const t=e.target.nick,n=t.value;n?(t.classList.remove("error"),this.el.querySelector(".chatroom-form-container").outerHTML=uo()(),this.join(n)):t.classList.add("error")},checkForReservedNick(){this.showSpinner(),this.model.checkForReservedNick().then(this.onReservedNickFound.bind(this)).catch(this.onReservedNickNotFound.bind(this))},onReservedNickFound(e){this.model.get("nick")?this.join():this.onReservedNickNotFound()},onReservedNickNotFound(e){const t=this.model.getDefaultNick();t?this.join(t):this.renderNicknameForm(e)},onNicknameClash(n){if(e.muc_nickname_from_jid){const e=n.getAttribute("from").split("/")[1];if(e===this.model.getDefaultNick())this.join(e+"-2");else{const t=e.lastIndexOf("-"),n=e.substring(t+1,e.length);this.join(e.substring(0,t+1)+String(Number(n)+1))}}else this.renderNicknameForm(t("The nickname you chose is reserved or currently in use, please choose a different one."))},hideChatRoomContents(){const e=this.el.querySelector(".chatroom-body");rs.isNull(e)||rs.each(e.children,e=>{e.classList.add("hidden")})},renderNicknameForm(e){this.hideChatRoomContents(),rs.each(this.el.querySelectorAll("span.centered.spinner"),us.removeElement),rs.isString(e)||(e=""),this.el.querySelector(".chatroom-body").insertAdjacentHTML("beforeend",Pr()({heading:t("Please choose your nickname"),label_nickname:t("Nickname"),label_join:t("Enter groupchat"),validation_message:e})),this.model.save("connection_status",P.ROOMSTATUS.NICKNAME_REQUIRED),this.el.querySelector(".chatroom-form").addEventListener("submit",this.submitNickname.bind(this),!1)},submitPassword(e){e.preventDefault();const t=this.el.querySelector(".chatroom-form input[type=password]").value;this.showSpinner(),this.join(this.model.get("nick"),t)},renderPasswordForm(){const e=this.el.querySelector(".chatroom-body");rs.each(e.children,us.hideElement),rs.each(this.el.querySelectorAll(".spinner"),us.removeElement),rs.each(this.el.querySelectorAll(".chatroom-form-container"),us.removeElement),e.insertAdjacentHTML("beforeend",zr()({heading:t("This groupchat requires a password"),label_password:t("Password: "),label_submit:t("Submit")})),this.model.save("connection_status",P.ROOMSTATUS.PASSWORD_REQUIRED),this.el.querySelector(".chatroom-form").addEventListener("submit",e=>this.submitPassword(e),!1)},showDestroyedMessage(e){us.hideElement(this.el.querySelector(".chat-area")),us.hideElement(this.el.querySelector(".occupants")),rs.each(this.el.querySelectorAll(".spinner"),us.removeElement);const n=this.el.querySelector(".disconnect-container"),o=rs.get(as('gone[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]',e).pop(),"textContent").replace(/^xmpp:/,"").replace(/\?join$/,""),a=rs.get(as('text[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]',e).pop(),"textContent");n.innerHTML=xr()({_:rs,__:t,jid:o,reason:a?`"${a}"`:null});const r=n.querySelector("a.switch-chat");r&&r.addEventListener("click",e=>{e.preventDefault(),this.model.save("jid",o),n.innerHTML="",this.showSpinner(),this.enterRoom()}),us.showElement(n)},showDisconnectMessages(e){rs.isString(e)&&(e=[e]),us.hideElement(this.el.querySelector(".chat-area")),us.hideElement(this.el.querySelector(".occupants")),rs.each(this.el.querySelectorAll(".spinner"),us.removeElement);const t=this.el.querySelector(".disconnect-container");t.innerHTML=Cr()({_:rs,disconnect_messages:e}),us.showElement(t)},getMessageFromStatus(n,o,a){const r=n.getAttribute("code");if("110"===r||"100"===r&&!a)return;if(r in e.muc.info_messages)return e.muc.info_messages[r];let s;if(a){if(r in e.muc.new_nickname_messages)return a&&"210"===r?s=ns.getResourceFromJid(o.getAttribute("from")):a&&"303"===r&&(s=o.querySelector("x item").getAttribute("nick")),t(e.muc.new_nickname_messages[r],s)}else if(r in e.muc.action_info_messages)return s=ns.getResourceFromJid(o.getAttribute("from")),t(e.muc.action_info_messages[r],s)},getNotificationWithMessage(e){let t=this.content.lastElementChild;for(;!rs.isNil(t);){rs.get(t,"dataset",{});if(!rs.includes(rs.get(t,"classList",[]),"chat-info"))return;if(t.textContent===e)return t;t=t.previousElementSibling}},parseXUserElement(t,n,o){const a=t.querySelectorAll("status"),r=rs.partial(this.getMessageFromStatus,rs,n,o),s={},c=rs.reject(rs.reject(rs.map(a,r),rs.isUndefined),e=>this.getNotificationWithMessage(e));c.length&&(s.messages=c);const f=rs.invokeMap(a,Element.prototype.getAttribute,"code"),i=rs.intersection(f,rs.keys(e.muc.disconnect_messages));o&&i.length>0&&(s.disconnected=!0,s.disconnection_message=e.muc.disconnect_messages[i[0]]);const u=t.querySelector("item");if(!rs.isNull(u)){const e=u.querySelector("reason");e&&(s.reason=e?e.textContent:void 0);const t=u.querySelector("actor");t&&(s.actor=t?t.getAttribute("nick"):void 0)}return s},showNotificationsforUser(e){if(e.disconnected){const n=[];return n.push(e.disconnection_message),e.actor&&n.push(t("This action was done by %1$s.",e.actor)),e.reason&&n.push(t('The reason given is: "%1$s".',e.reason)),this.showDisconnectMessages(n),void this.model.save("connection_status",P.ROOMSTATUS.DISCONNECTED)}rs.each(e.messages,e=>{this.content.insertAdjacentHTML("beforeend",Tn()({isodate:os().format(),extra_classes:"chat-event",message:e}))}),e.reason&&this.showChatEvent(t('The reason given is: "%1$s".',e.reason)),rs.get(e.messages,"length")&&this.scrollDown()},onOccupantAdded(e){"online"===e.get("show")&&this.showJoinNotification(e)},onOccupantRemoved(e){"online"===e.get("show")&&this.showLeaveNotification(e)},showJoinOrLeaveNotification(e){rs.includes(e.get("states"),"303")||("offline"===e.get("show")?this.showLeaveNotification(e):"online"===e.get("show")&&this.showJoinNotification(e))},getPreviousJoinOrLeaveNotification(e,t){for(;!rs.isNil(e);){const n=rs.get(e,"dataset",{});if(!rs.includes(rs.get(e,"classList",[]),"chat-info"))return;if(os(e.getAttribute("data-isodate")).isSame(new Date,"day")){if(n.join===t||n.leave===t||n.leavejoin===t||n.joinleave===t)return e;e=e.previousElementSibling}else e=e.previousElementSibling}},showJoinNotification(n){if(!e.muc_show_join_leave||this.model.get("connection_status")!==P.ROOMSTATUS.ENTERED)return;const o=n.get("nick"),a=n.get("status"),r=this.getPreviousJoinOrLeaveNotification(this.content.lastElementChild,o);if(rs.get(r,"dataset",{}).leave===o){let e;e=rs.isNil(a)?t("%1$s has left and re-entered the groupchat",o):t('%1$s has left and re-entered the groupchat. "%2$s"',o,a);const n={data_name:"leavejoin",data_value:o,isodate:os().format(),extra_classes:"chat-event",message:e};this.content.removeChild(r),this.content.insertAdjacentHTML("beforeend",Tn()(n));const s=this.content.lastElementChild;setTimeout(()=>us.addClass("fade-out",s),5e3),setTimeout(()=>s.parentElement&&s.parentElement.removeChild(s),5500)}else{let e;e=rs.isNil(a)?t("%1$s has entered the groupchat",o):t('%1$s has entered the groupchat. "%2$s"',o,a);const n={data_name:"join",data_value:o,isodate:os().format(),extra_classes:"chat-event",message:e};r?(this.content.removeChild(r),this.content.insertAdjacentHTML("beforeend",Tn()(n))):(this.content.insertAdjacentHTML("beforeend",Tn()(n)),this.insertDayIndicator(this.content.lastElementChild))}this.scrollDown()},showLeaveNotification(n){if(!e.muc_show_join_leave||rs.includes(n.get("states"),"303")||rs.includes(n.get("states"),"307"))return;const o=n.get("nick"),a=n.get("status"),r=this.getPreviousJoinOrLeaveNotification(this.content.lastElementChild,o);if(rs.get(r,"dataset",{}).join===o){let e;e=rs.isNil(a)?t("%1$s has entered and left the groupchat",o):t('%1$s has entered and left the groupchat. "%2$s"',o,a);const n={data_name:"joinleave",data_value:o,isodate:os().format(),extra_classes:"chat-event",message:e};this.content.removeChild(r),this.content.insertAdjacentHTML("beforeend",Tn()(n));const s=this.content.lastElementChild;setTimeout(()=>us.addClass("fade-out",s),5e3),setTimeout(()=>s.parentElement&&s.parentElement.removeChild(s),5500)}else{let e;const n={message:e=rs.isNil(a)?t("%1$s has left the groupchat",o):t('%1$s has left the groupchat. "%2$s"',o,a),isodate:os().format(),extra_classes:"chat-event",data_name:"leave",data_value:o};r?(this.content.removeChild(r),this.content.insertAdjacentHTML("beforeend",Tn()(n))):(this.content.insertAdjacentHTML("beforeend",Tn()(n)),this.insertDayIndicator(this.content.lastElementChild))}this.scrollDown()},showStatusMessages(e){const t=as(`x[xmlns="${ns.NS.MUC_USER}"]`,e),n=e.querySelectorAll("status[code='110']").length,o=rs.partial(this.parseXUserElement.bind(this),rs,e,n),a=rs.reject(rs.map(t,o),rs.isEmpty);rs.each(a,this.showNotificationsforUser.bind(this))},showErrorMessageFromPresence(e){const n=e.querySelector("error");if("auth"===n.getAttribute("type"))rs.isNull(n.querySelector("not-authorized"))?rs.isNull(n.querySelector("registration-required"))?rs.isNull(n.querySelector("forbidden"))||this.showDisconnectMessages(t("You have been banned from this groupchat.")):this.showDisconnectMessages(t("You are not on the member list of this groupchat.")):this.renderPasswordForm();else if("modify"===n.getAttribute("type"))rs.isNull(n.querySelector("jid-malformed"))||this.showDisconnectMessages(t("No nickname was specified."));else if("cancel"===n.getAttribute("type"))if(rs.isNull(n.querySelector("not-allowed")))if(rs.isNull(n.querySelector("not-acceptable")))if(as('gone[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]',n).length)this.showDestroyedMessage(n);else if(rs.isNull(n.querySelector("conflict")))if(rs.isNull(n.querySelector("item-not-found")))if(rs.isNull(n.querySelector("service-unavailable"))){if(!rs.isNull(n.querySelector("remote-server-not-found"))){const e=[t("Remote server not found")],o=rs.get(n.querySelector("text"),"textContent");o&&e.push(t('The explanation given is: "%1$s".',o)),this.showDisconnectMessages(e)}}else this.showDisconnectMessages(t("This groupchat has reached its maximum number of participants."));else this.showDisconnectMessages(t("This groupchat does not (yet) exist."));else this.onNicknameClash(e);else this.showDisconnectMessages(t("Your nickname doesn't conform to this groupchat's policies."));else this.showDisconnectMessages(t("You are not allowed to create new groupchats."))},renderAfterTransition(){this.model.get("connection_status")==P.ROOMSTATUS.NICKNAME_REQUIRED?this.renderNicknameForm():this.model.get("connection_status")==P.ROOMSTATUS.PASSWORD_REQUIRED?this.renderPasswordForm():(this.el.querySelector(".chat-area").classList.remove("hidden"),this.setOccupantsVisibility(),this.scrollDown())},showSpinner(){us.removeElement(this.el.querySelector(".spinner"));const e=this.el.querySelector(".chatroom-body"),t=Array.prototype.slice.call(e.children,0);e.insertAdjacentHTML("afterbegin",uo()()),rs.each(t,us.hideElement)},hideSpinner(){const e=this.el.querySelector(".spinner");return rs.isNull(e)||(us.removeElement(e),this.renderAfterTransition()),this},setChatRoomSubject(){const e=this.model.get("subject"),n=e.text?t("Topic set by %1$s",e.author):t("Topic cleared by %1$s",e.author),o=os().format();this.content.insertAdjacentHTML("beforeend",Tn()({isodate:o,extra_classes:"chat-event",message:n})),e.text&&this.content.insertAdjacentHTML("beforeend",Tn()({isodate:o,extra_classes:"chat-topic",message:us.addHyperlinks(Rn.a.filterXSS(rs.get(this.model.get("subject"),"text"),{whiteList:{}})),render_message:!0})),this.scrollDown()}}),e.RoomsPanel=ts.NativeView.extend({tagName:"div",className:"controlbox-section",id:"chatrooms",events:{"click a.controlbox-heading__btn.show-add-muc-modal":"showAddRoomModal","click a.controlbox-heading__btn.show-list-muc-modal":"showListRoomsModal"},render(){return this.el.innerHTML=Xr()({heading_chatrooms:t("Groupchats"),title_new_room:t("Add a new groupchat"),title_list_rooms:t("Query for groupchats")}),this},showAddRoomModal(t){rs.isUndefined(this.add_room_modal)&&(this.add_room_modal=new e.AddChatRoomModal({model:this.model})),this.add_room_modal.show(t)},showListRoomsModal(t){rs.isUndefined(this.list_rooms_modal)&&(this.list_rooms_modal=new e.ListChatRoomsModal({model:this.model})),this.list_rooms_modal.show(t)}}),e.ChatRoomOccupantView=ts.VDOMView.extend({tagName:"li",initialize(){this.model.on("change",this.render,this)},toHTML(){const n=this.model.get("show");return $r()(rs.extend({_:rs,jid:"",show:n,hint_show:e.PRETTY_CHAT_STATUS[n],hint_occupant:t("Click to mention %1$s in your message.",this.model.get("nick")),desc_moderator:t("This user is a moderator."),desc_participant:t("This user can send messages in this groupchat."),desc_visitor:t("This user can NOT send messages in this groupchat."),label_moderator:t("Moderator"),label_visitor:t("Visitor"),label_owner:t("Owner"),label_member:t("Member"),label_admin:t("Admin")},this.model.toJSON()))},destroy(){this.el.parentElement.removeChild(this.el)}}),e.ChatRoomOccupantsView=ts.OrderedListView.extend({tagName:"div",className:"occupants col-md-3 col-4",listItems:"model",sortEvent:"change:role",listSelector:".occupant-list",ItemView:e.ChatRoomOccupantView,initialize(){ts.OrderedListView.prototype.initialize.apply(this,arguments),this.chatroomview=this.model.chatroomview,this.chatroomview.model.on("change:affiliation",this.renderInviteWidget,this),this.chatroomview.model.features.on("change:open",this.renderInviteWidget,this),this.chatroomview.model.features.on("change",this.renderRoomFeatures,this),this.render(),this.model.fetch({add:!0,silent:!0,success:this.sortAndPositionAllItems.bind(this)})},render(){return this.el.innerHTML=Br()(rs.extend(this.chatroomview.model.toJSON(),{allow_muc_invitations:e.allow_muc_invitations,label_occupants:t("Participants")})),e.allow_muc_invitations&&e.api.waitUntil("rosterContactsFetched").then(this.renderInviteWidget.bind(this)),this.renderRoomFeatures()},renderInviteWidget(){const e=this.el.querySelector("form.room-invite");if(this.shouldInviteWidgetBeShown()){if(rs.isNull(e)){this.el.querySelector(".occupants-heading").insertAdjacentHTML("afterend",Rr()({error_message:null,label_invitation:t("Invite")})),this.initInviteWidget()}}else rs.isNull(e)||e.remove();return this},renderRoomFeatures(){const e=this.chatroomview.model.features,n=rs.pick(e.attributes,P.ROOM_FEATURES);if(rs.reduce(rs.values(n),(e,t)=>e||t)){this.el.querySelector(".chatroom-features").innerHTML=Tr()(rs.extend(e.toJSON(),{__:t})),this.setOccupantsHeight()}return this},setOccupantsHeight(){const e=this.el.querySelector(".chatroom-features");this.el.querySelector(".occupant-list").style.cssText=`height: calc(100% - ${e.offsetHeight}px - 5em);`},promptForInvite(e){const n=prompt(t('You are about to invite %1$s to the groupchat "%2$s". You may optionally include a message, explaining the reason for the invitation.',e.text.label,this.model.get("id")));null!==n&&this.chatroomview.model.directInvite(e.text.value,n);const o=e.target.form.querySelector(".pure-form-message.error");rs.isNull(o)||o.parentNode.removeChild(o),e.target.value=""},inviteFormSubmitted(e){e.preventDefault();const n=e.target.querySelector("input.invited-contact"),o=n.value;if(!o||rs.compact(o.split("@")).length<2)return e.target.outerHTML=Rr()({error_message:t("Please enter a valid XMPP address"),label_invitation:t("Invite")}),void this.initInviteWidget();this.promptForInvite({target:n,text:{label:o,value:o}})},shouldInviteWidgetBeShown(){return e.allow_muc_invitations&&(this.chatroomview.model.features.get("open")||"owner"===this.chatroomview.model.get("affiliation"))},initInviteWidget(){const t=this.el.querySelector("form.room-invite");if(rs.isNull(t))return;t.addEventListener("submit",this.inviteFormSubmitted.bind(this),!1);const n=this.el.querySelector("input.invited-contact"),o=e.roster.map(function(e){return{label:e.get("fullname")||e.get("jid"),value:e.get("jid")}});new Zo.a(n,{minChars:1,list:o});n.addEventListener("awesomplete-selectcomplete",this.promptForInvite.bind(this))}}),e.on("chatBoxViewsInitialized",()=>{e.chatboxviews.delegate("click","a.open-chatroom",function(t){t.preventDefault(),e.api.rooms.open(t.target.href)});const t=e.chatboxviews;e.chatboxes.on("add",n=>{if(!t.get(n.get("id"))&&n.get("type")===e.CHATROOMS_TYPE)return t.add(n.get("id"),new e.ChatRoomView({model:n}))})}),e.api.listen.on("clearSession",()=>{const t=e.chatboxviews.get("controlbox");t&&t.roomspanel&&(t.roomspanel.remove(),delete t.roomspanel)}),e.api.listen.on("controlboxInitialized",t=>{e.allow_muc&&(r(t),t.model.on("change:connected",rs.partial(r,t)))}),e.on("reconnected",function(){e.chatboxviews.each(function(t){t.model.get("type")===e.CHATROOMS_TYPE&&(t.model.save("connection_status",P.ROOMSTATUS.DISCONNECTED),t.model.registerHandlers(),t.populateAndJoin())})}),rs.extend(e.api,{roomviews:{close(t){if(rs.isUndefined(t))e.chatboxviews.each(function(e){e.is_chatroom&&e.model&&e.close()});else if(rs.isString(t)){const n=e.chatboxviews.get(t);n&&n.close()}else rs.each(t,function(t){const n=e.chatboxviews.get(t);n&&n.close()})}}})}});const ds=P.env,ls=ds.Strophe,hs=ds._,ms=ds.sizzle,gs=P.env.utils;P.plugins.add("converse-notification",{dependencies:["converse-chatboxes"],initialize(){const e=this._converse,t=e.__;e.supports_html5_notification="Notification"in window,e.api.settings.update({notify_all_room_messages:!1,show_desktop_notifications:!0,show_chatstate_notifications:!1,chatstate_notification_blacklist:[],play_sounds:!0,sounds_path:"sounds/",notification_icon:"logo/conversejs-filled.svg",notification_delay:5e3}),e.isOnlyChatStateNotification=(t=>hs.isNull(t.querySelector("body"))&&(hs.isNull(t.querySelector(e.ACTIVE))||hs.isNull(t.querySelector(e.COMPOSING))||hs.isNull(t.querySelector(e.INACTIVE))||hs.isNull(t.querySelector(e.PAUSED))||hs.isNull(t.querySelector(e.GONE)))),e.shouldNotifyOfGroupMessage=function(t){let n=e.notify_all_room_messages;const o=t.getAttribute("from"),a=ls.getResourceFromJid(o),r=ls.getBareJidFromJid(o),s=a&&ls.unescapeNode(a)||"";if(""===s||t.querySelectorAll("delay").length>0)return!1;const c=e.chatboxes.get(r),f=t.querySelector("body");if(hs.isNull(f))return!1;const i=new RegExp(`\\b${c.get("nick")}\\b`).test(f.textContent);return n=!0===n||hs.isArray(n)&&hs.includes(n,r),!(s===c.get("nick")||!n&&!i)},e.isMessageToHiddenChat=function(t){if(e.isUniView()){const n=ls.getBareJidFromJid(t.getAttribute("from")),o=e.chatboxviews.get(n);return!!hs.isNil(o)||(o.model.get("hidden")||"hidden"===e.windowState||!gs.isVisible(o.el))}return"hidden"===e.windowState},e.shouldNotifyOfMessage=function(t){const n=t.querySelector("forwarded");if(!hs.isNull(n))return!1;if("groupchat"===t.getAttribute("type"))return e.shouldNotifyOfGroupMessage(t);if(gs.isHeadlineMessage(e,t))return e.isMessageToHiddenChat(t);const o=ls.getBareJidFromJid(t.getAttribute("from"))===e.bare_jid;return!e.isOnlyChatStateNotification(t)&&!o&&("all"===e.show_desktop_notifications||e.isMessageToHiddenChat(t))},e.playSoundNotification=function(){if(e.play_sounds&&!hs.isUndefined(window.Audio)){const t=new Audio(e.sounds_path+"msg_received.ogg"),n=t.canPlayType("audio/ogg");if("probably"===n)return t.play();const o=new Audio(e.sounds_path+"msg_received.mp3"),a=o.canPlayType("audio/mp3");"probably"===a?o.play():"maybe"===n?t.play():"maybe"===a&&o.play()}},e.areDesktopNotificationsEnabled=function(){return e.supports_html5_notification&&e.show_desktop_notifications&&"granted"===Notification.permission},e.showMessageNotification=function(n){if(!e.areDesktopNotificationsEnabled())return;let o,a;const r=n.getAttribute("from"),s=ls.getBareJidFromJid(r);if("headline"===n.getAttribute("type")){if(hs.includes(s,"@")&&!e.allow_non_roster_messaging)return;o=t("Notification from %1$s",s)}else if(hs.includes(s,"@"))if("groupchat"===n.getAttribute("type"))o=t("%1$s says",ls.getResourceFromJid(r));else{if(hs.isUndefined(e.roster))return void e.log("Could not send notification, because roster is undefined",ls.LogLevel.ERROR);if(a=e.roster.get(s),hs.isUndefined(a)){if(!e.allow_non_roster_messaging)return;o=t("%1$s says",s)}else o=t("%1$s says",a.getDisplayName())}else o=t("Notification from %1$s",s);const c=ms(`encrypted[xmlns="${ls.NS.OMEMO}"]`,n).length?t("OMEMO Message received"):hs.get(n.querySelector("body"),"textContent");if(!c)return;const f=new Notification(o,{body:c,lang:e.locale,icon:e.notification_icon,requireInteraction:!e.notification_delay});e.notification_delay&&setTimeout(f.close.bind(f),e.notification_delay)},e.showChatStateNotification=function(n){if(hs.includes(e.chatstate_notification_blacklist,n.jid))return;const o=n.chat_status;let a=null;if("offline"===o?a=t("has gone offline"):"away"===o?a=t("has gone away"):"dnd"===o?a=t("is busy"):"online"===o&&(a=t("has come online")),null===a)return;const r=new Notification(n.getDisplayName(),{body:a,lang:e.locale,icon:e.notification_icon});setTimeout(r.close.bind(r),5e3)},e.showContactRequestNotification=function(n){const o=new Notification(n.getDisplayName(),{body:t("wants to be your contact"),lang:e.locale,icon:e.notification_icon});setTimeout(o.close.bind(o),5e3)},e.showFeedbackNotification=function(t){if("error"===t.klass||"warn"===t.klass){const n=new Notification(t.subject,{body:t.message,lang:e.locale,icon:e.notification_icon});setTimeout(n.close.bind(n),5e3)}},e.handleChatStateNotification=function(t){e.areDesktopNotificationsEnabled()&&e.show_chatstate_notifications&&e.showChatStateNotification(t)},e.handleMessageNotification=function(t){const n=t.stanza;if(!e.shouldNotifyOfMessage(n))return!1;e.api.emit("messageNotification",n),e.playSoundNotification(),e.showMessageNotification(n)},e.handleContactRequestNotification=function(t){e.areDesktopNotificationsEnabled(!0)&&e.showContactRequestNotification(t)},e.handleFeedback=function(t){e.areDesktopNotificationsEnabled(!0)&&e.showFeedbackNotification(t)},e.requestPermission=function(){e.supports_html5_notification&&!hs.includes(["denied","granted"],Notification.permission)&&Notification.requestPermission()},e.on("pluginsInitialized",function(){e.on("contactRequest",e.handleContactRequestNotification),e.on("contactPresenceChanged",e.handleChatStateNotification),e.on("message",e.handleMessageNotification),e.on("feedback",e.handleFeedback),e.on("connected",e.requestPermission)})}});var ps=n(46),ys=n.n(ps);const bs=P.env,vs=bs.Backbone,ws=bs.Promise,ks=bs.Strophe,xs=(bs.moment,bs.sizzle),Ss=bs.$build,Ms=bs.$iq,As=bs.$msg,Cs=bs._,Es=bs.f,Ts=P.env.utils;ks.addNamespace("OMEMO_DEVICELIST",ks.NS.OMEMO+".devicelist"),ks.addNamespace("OMEMO_VERIFICATION",ks.NS.OMEMO+".verification"),ks.addNamespace("OMEMO_WHITELISTED",ks.NS.OMEMO+".whitelisted"),ks.addNamespace("OMEMO_BUNDLES",ks.NS.OMEMO+".bundles");const Ls={name:"AES-GCM",length:128};class js extends Error{constructor(e,t){super(e,t),this.name="IQError",this.iq=t}}function Ns(e){const t=e.querySelector("signedPreKeyPublic"),n=e.querySelector("signedPreKeySignature"),o=(e.querySelector("identityKey"),Cs.map(xs("prekeys > preKeyPublic",e),e=>({id:parseInt(e.getAttribute("preKeyId"),10),key:e.textContent})));return{identity_key:e.querySelector("identityKey").textContent.trim(),signed_prekey:{id:parseInt(t.getAttribute("signedPreKeyId"),10),public_key:t.textContent,signature:n.textContent},prekeys:o}}P.plugins.add("converse-omemo",{enabled:e=>!Cs.isNil(window.libsignal)&&!Es.includes("converse-omemo",e.blacklisted_plugins),dependencies:["converse-chatview","converse-pubsub"],overrides:{ProfileModal:{events:{"change input.select-all":"selectAll","click .generate-bundle":"generateOMEMODeviceBundle","submit .fingerprint-removal":"removeSelectedFingerprints"},initialize(){const e=this.__super__._converse;return this.debouncedRender=Cs.debounce(this.render,50),this.devicelist=e.devicelists.get(e.bare_jid),this.devicelist.devices.on("change:bundle",this.debouncedRender,this),this.devicelist.devices.on("reset",this.debouncedRender,this),this.devicelist.devices.on("reset",this.debouncedRender,this),this.devicelist.devices.on("remove",this.debouncedRender,this),this.devicelist.devices.on("add",this.debouncedRender,this),this.__super__.initialize.apply(this,arguments)},beforeRender(){const e=this.__super__._converse.omemo_store.get("device_id");if(e&&(this.current_device=this.devicelist.devices.get(e)),this.other_devices=this.devicelist.devices.filter(t=>t.get("id")!==e),this.__super__.beforeRender)return this.__super__.beforeRender.apply(this,arguments)},selectAll(e){let t=Ts.ancestor(e.target,"li");for(;t;)t.querySelector('input[type="checkbox"]').checked=e.target.checked,t=t.nextElementSibling},removeSelectedFingerprints(e){e.preventDefault(),e.stopPropagation(),e.target.querySelector(".select-all").checked=!1;const t=e.target.querySelectorAll('.fingerprint-removal-item input[type="checkbox"]:checked'),n=Cs.map(t,"value");this.devicelist.removeOwnDevices(n).then(this.modal.hide).catch(e=>{const t=this.__super__._converse,n=t.__;t.log(e,ks.LogLevel.ERROR),t.api.alert.show(ks.LogLevel.ERROR,n("Error"),[n("Sorry, an error occurred while trying to remove the devices.")])})},generateOMEMODeviceBundle(e){const t=this.__super__._converse,n=t.__,o=t.api;e.preventDefault(),confirm(n("Are you sure you want to generate new OMEMO keys? This will remove your old keys and all previously encrypted messages will no longer be decryptable on this device."))&&o.omemo.bundle.generate()}},UserDetailsModal:{events:{"click .fingerprint-trust .btn input":"toggleDeviceTrust"},initialize(){const e=this.__super__._converse,t=this.model.get("jid");return this.devicelist=e.devicelists.get(t)||e.devicelists.create({jid:t}),this.devicelist.devices.on("change:bundle",this.render,this),this.devicelist.devices.on("change:trusted",this.render,this),this.devicelist.devices.on("remove",this.render,this),this.devicelist.devices.on("add",this.render,this),this.devicelist.devices.on("reset",this.render,this),this.__super__.initialize.apply(this,arguments)},toggleDeviceTrust(e){const t=e.target;this.devicelist.devices.get(t.getAttribute("name")).save("trusted",parseInt(t.value,10))}},ChatBox:{async encryptMessage(e){const t=crypto.getRandomValues(new window.Uint8Array(12)),n=await crypto.subtle.generateKey(Ls,!0,["encrypt","decrypt"]),o={name:"AES-GCM",iv:t,tagLength:128},a=await crypto.subtle.encrypt(o,n,Ts.stringToArrayBuffer(e)),r=a.byteLength-16,s=a.slice(0,r),c=a.slice(r),f=await crypto.subtle.exportKey("raw",n);return ws.resolve({key:f,tag:c,key_and_tag:Ts.appendArrayBuffer(f,c),payload:Ts.arrayBufferToBase64(s),iv:Ts.arrayBufferToBase64(t)})},async decryptMessage(e){const t=await crypto.subtle.importKey("raw",e.key,Ls,!0,["encrypt","decrypt"]),n=Ts.appendArrayBuffer(Ts.base64ToArrayBuffer(e.payload),e.tag),o={name:"AES-GCM",iv:Ts.base64ToArrayBuffer(e.iv),tagLength:128};return Ts.arrayBufferToString(await crypto.subtle.decrypt(o,t,n))},reportDecryptionError(e){const t=this.__super__._converse;if(t.debug){const n=t.__;this.messages.create({message:n("Sorry, could not decrypt a received OMEMO message due to an error.")+` ${e.name} ${e.message}`,type:"error"})}t.log(`${e.name} ${e.message}`,ks.LogLevel.ERROR)},decrypt(e){const t=this.__super__._converse,n=this.getSessionCipher(e.from,parseInt(e.encrypted.device_id,10));if("true"===e.encrypted.prekey){let o;return n.decryptPreKeyWhisperMessage(Ts.base64ToArrayBuffer(e.encrypted.key),"binary").then(t=>{if(e.encrypted.payload){const n=t.slice(0,16),o=t.slice(16);return this.decryptMessage(Cs.extend(e.encrypted,{key:n,tag:o}))}return ws.resolve()}).then(e=>(o=e,t.omemo_store.generateMissingPreKeys())).then(()=>t.omemo_store.publishBundle()).then(()=>o?Cs.extend(e,{plaintext:o}):Cs.extend(e,{is_only_key:!0})).catch(t=>(this.reportDecryptionError(t),e))}return n.decryptWhisperMessage(Ts.base64ToArrayBuffer(e.encrypted.key),"binary").then(t=>{const n=t.slice(0,16),o=t.slice(16);return this.decryptMessage(Cs.extend(e.encrypted,{key:n,tag:o}))}).then(t=>Cs.extend(e,{plaintext:t})).catch(t=>(this.reportDecryptionError(t),e))},getEncryptionAttributesfromStanza(e,t,n){const o=this.__super__._converse,a=xs(`encrypted[xmlns="${ks.NS.OMEMO}"]`,t).pop(),r=a.querySelector("header"),s=xs(`key[rid="${o.omemo_store.get("device_id")}"]`,a).pop();return s?(n.is_encrypted=!0,n.encrypted={device_id:r.getAttribute("sid"),iv:r.querySelector("iv").textContent,key:s.textContent,payload:Cs.get(a.querySelector("payload"),"textContent",null),prekey:s.getAttribute("prekey")},this.decrypt(n)):ws.resolve(n)},async getMessageAttributesFromStanza(e,t){const n=this.__super__._converse,o=xs(`encrypted[xmlns="${ks.NS.OMEMO}"]`,t).pop(),a=await this.__super__.getMessageAttributesFromStanza.apply(this,arguments);return o&&n.config.get("trusted")?this.getEncryptionAttributesfromStanza(e,t,a):a},getSessionCipher(e,t){const n=this.__super__._converse,o=new libsignal.SignalProtocolAddress(e,t);return this.session_cipher=new window.libsignal.SessionCipher(n.omemo_store,o),this.session_cipher},encryptKey(e,t){return this.getSessionCipher(t.get("jid"),t.get("id")).encrypt(e).then(e=>({payload:e,device:t}))},handleMessageSendError(e){const t=this.__super__._converse,n=t.__;if("IQError"!==e.name)throw e;{this.save("omemo_supported",!1);const o=[];xs(`presence-subscription-required[xmlns="${ks.NS.PUBSUB_ERROR}"]`,e.iq).length?o.push(n("Sorry, we're unable to send an encrypted message because %1$s requires you to be subscribed to their presence in order to see their OMEMO information",e.iq.getAttribute("from"))):xs('remote-server-not-found[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]',e.iq).length?o.push(n("Sorry, we're unable to send an encrypted message because the remote server for %1$s could not be found",e.iq.getAttribute("from"))):(o.push(n("Unable to send an encrypted message due to an unexpected error.")),o.push(e.iq.outerHTML)),t.api.alert.show(ks.LogLevel.ERROR,n("Error"),o),t.log(e,ks.LogLevel.ERROR)}},async sendMessage(e){const t=this.__super__._converse;t.__;if(this.get("omemo_active")&&e.message){e.is_encrypted=!0,e.plaintext=e.message;try{const n=await t.getBundlesAndBuildSessions(this),o=await t.createOMEMOMessageStanza(this,this.messages.create(e),n);this.sendMessageStanza(o)}catch(e){return this.handleMessageSendError(e),!1}return!0}return this.__super__.sendMessage.apply(this,arguments)}},ChatBoxView:{events:{"click .toggle-omemo":"toggleOMEMO"},initialize(){this.__super__.initialize.apply(this,arguments),this.model.on("change:omemo_active",this.renderOMEMOToolbarButton,this),this.model.on("change:omemo_supported",this.onOMEMOSupportedDetermined,this)},showMessage(e){if(!e.get("is_only_key"))return this.__super__.showMessage.apply(this,arguments)},onOMEMOSupportedDetermined(){!this.model.get("omemo_supported")&&this.model.get("omemo_active")?this.model.set("omemo_active",!1):this.renderOMEMOToolbarButton()},renderOMEMOToolbarButton(){const e=this.__super__._converse.__,t=this.el.querySelector(".toggle-omemo"),n=ys()(Cs.extend(this.model.toJSON(),{__:e}));t?t.outerHTML=n:this.el.querySelector(".chat-toolbar").insertAdjacentHTML("beforeend",n)},toggleOMEMO(e){const t=this.__super__._converse,n=t.__;if(!this.model.get("omemo_supported"))return t.api.alert.show(ks.LogLevel.ERROR,n("Error"),[n("Cannot use end-to-end encryption because %1$s uses a client that doesn't support OMEMO.",this.model.contact.getDisplayName())]);e.preventDefault(),this.model.save({omemo_active:!this.model.get("omemo_active")})}},ChatRoomView:{events:{"click .toggle-omemo":"toggleOMEMO"},initialize(){this.__super__.initialize.apply(this,arguments),this.model.on("change:omemo_active",this.renderOMEMOToolbarButton,this),this.model.on("change:omemo_supported",this.onOMEMOSupportedDetermined,this)},toggleOMEMO(e){const t=this.__super__._converse,n=t.__;if(!this.model.get("omemo_supported"))return t.api.alert.show(ks.LogLevel.ERROR,n("Error"),[n("Cannot use end-to-end encryption in this groupchat, either the groupchat has some anonymity or not all participants support OMEMO.")]);e.preventDefault(),this.model.save({omemo_active:!this.model.get("omemo_active")})},renderOMEMOToolbarButton(){if(this.model.features.get("membersonly")&&this.model.features.get("nonanonymous"))this.__super__.renderOMEMOToolbarButton.apply(arguments);else{const e=this.el.querySelector(".toggle-omemo");e&&e.parentElement.removeChild(e)}}}},initialize(){const e=this._converse,t=e.__;async function n(e){if(Cs.get(e.get("bundle"),"fingerprint"))return;const t=await e.getBundle();t.fingerprint=Ts.arrayBufferToHex(Ts.base64ToArrayBuffer(t.identity_key)),e.save("bundle",t),e.trigger("change:bundle")}async function o(t){await e.api.waitUntil("OMEMOInitialized");const n=e.devicelists.get(t)||e.devicelists.create({jid:t});return await n.fetchDevices(),n.devices}function a(t){const n=new libsignal.SignalProtocolAddress(t.get("jid"),t.get("id"));return e.omemo_store.loadSession(n.toString()).then(n=>n?ws.resolve():async function(t){const n=new libsignal.SignalProtocolAddress(t.get("jid"),t.get("id")),o=new libsignal.SessionBuilder(e.omemo_store,n),a=t.getRandomPreKey(),r=await t.getBundle();return o.processPreKey({registrationId:parseInt(t.get("id"),10),identityKey:Ts.base64ToArrayBuffer(r.identity_key),signedPreKey:{keyId:r.signed_prekey.id,publicKey:Ts.base64ToArrayBuffer(r.signed_prekey.public_key),signature:Ts.base64ToArrayBuffer(r.signed_prekey.signature)},preKey:{keyId:a.id,publicKey:Ts.base64ToArrayBuffer(a.key)}})}(t))}async function r(){await new ws((t,n)=>e.devicelists.fetch({success:t,error:n}));let t=e.devicelists.get(e.bare_jid);return Cs.isNil(t)&&(t=e.devicelists.create({jid:e.bare_jid})),t.fetchDevices()}function s(){if(Cs.isUndefined(e.omemo_store)){const t=e.config.get("storage"),n=`converse.omemosession-${e.bare_jid}`;e.omemo_store=new e.OMEMOStore({id:n}),e.omemo_store.browserStorage=new vs.BrowserStorage[t](n)}return e.omemo_store.fetchSession()}async function c(t){let n;t.get("type")===e.CHATROOMS_TYPE?(await e.api.waitUntil("OMEMOInitialized"),n=t.features.get("nonanonymous")&&t.features.get("membersonly")):t.get("type")===e.PRIVATE_CHAT_TYPE&&(n=await e.contactHasOMEMOSupport(t.get("jid"))),t.set("omemo_supported",n)}e.api.promises.add(["OMEMOInitialized"]),e.NUM_PREKEYS=100,e.generateFingerprints=async function(e){const t=await o(e);return ws.all(t.map(e=>n(e)))},e.getDeviceForContact=function(e,t){return o(e).then(e=>e.get(t))},e.contactHasOMEMOSupport=async function(e){return(await o(e)).length>0},e.getBundlesAndBuildSessions=async function(t){let n;const r=e.omemo_store.get("device_id");if(t.get("type")===e.CHATROOMS_TYPE){n=(await ws.all(t.occupants.map(e=>o(e.get("jid"))))).reduce((e,t)=>Cs.concat(e,t.models),[])}else if(t.get("type")===e.PRIVATE_CHAT_TYPE){const a=await o(t.get("jid")),s=e.devicelists.get(e.bare_jid).devices.filter(e=>e.get("id")!==r);n=Cs.concat(s,a.models)}return n=n.filter(e=>e.get("id")!==r),await ws.all(n.map(e=>e.getBundle())),await ws.all(n.map(e=>a(e))),n},e.createOMEMOMessageStanza=function(t,n,o){const a=(0,e.__)("This is an OMEMO encrypted message which your client doesn’t seem to support. Find more information on https://conversations.im/omemo");if(!n.get("message"))throw new Error("No message body to encrypt!");const r=As({from:e.connection.jid,to:t.get("jid"),type:t.get("message_type"),id:n.get("msgid")}).c("body").t(a).up();return"chat"===n.get("type")&&r.c("request",{xmlns:ks.NS.RECEIPTS}).up(),r.c("encrypted",{xmlns:ks.NS.OMEMO}).c("header",{sid:e.omemo_store.get("device_id")}),t.encryptMessage(n.get("message")).then(e=>{const n=o.filter(e=>-1!=e.get("trusted")).map(n=>t.encryptKey(e.key_and_tag,n));return ws.all(n).then(t=>(function(e,t,n){for(var o in t)if(Object.prototype.hasOwnProperty.call(t,o)){const a=t[o].payload,r=t[o].device,s=3==parseInt(a.type,10);e.c("key",{rid:r.get("id")}).t(btoa(a.body)),s&&e.attrs({prekey:s}),e.up(),o==t.length-1&&e.c("iv").t(n).up().up()}return ws.resolve(e)})(r,t,e.iv)).then(t=>(t.c("payload").t(e.payload).up().up(),t.c("store",{xmlns:ks.NS.HINTS}),t))})},e.OMEMOStore=vs.Model.extend({Direction:{SENDING:1,RECEIVING:2},getIdentityKeyPair(){const e=this.get("identity_keypair");return ws.resolve({privKey:Ts.base64ToArrayBuffer(e.privKey),pubKey:Ts.base64ToArrayBuffer(e.pubKey)})},getLocalRegistrationId(){return ws.resolve(parseInt(this.get("device_id"),10))},isTrustedIdentity(e,t,n){if(Cs.isNil(e))throw new Error("Can't check identity key for invalid key");if(!(t instanceof ArrayBuffer))throw new Error("Expected identity_key to be an ArrayBuffer");const o=this.get("identity_key"+e);return void 0===o?ws.resolve(!0):ws.resolve(Ts.arrayBufferToBase64(t)===o)},loadIdentityKey(e){if(Cs.isNil(e))throw new Error("Can't load identity_key for invalid identifier");return ws.resolve(Ts.base64ToArrayBuffer(this.get("identity_key"+e)))},saveIdentity(e,t){if(Cs.isNil(e))throw new Error("Can't save identity_key for invalid identifier");const n=new libsignal.SignalProtocolAddress.fromString(e),o=this.get("identity_key"+n.getName()),a=Ts.arrayBufferToBase64(t);return this.save("identity_key"+n.getName(),a),o&&a!==o?ws.resolve(!0):ws.resolve(!1)},getPreKeys(){return this.get("prekeys")||{}},loadPreKey(e){const t=this.getPreKeys()[e];return t?ws.resolve({privKey:Ts.base64ToArrayBuffer(t.privKey),pubKey:Ts.base64ToArrayBuffer(t.pubKey)}):ws.resolve()},storePreKey(e,t){const n={};return n[e]={pubKey:Ts.arrayBufferToBase64(t.pubKey),privKey:Ts.arrayBufferToBase64(t.privKey)},this.save("prekeys",Cs.extend(this.getPreKeys(),n)),ws.resolve()},removePreKey(e){return this.save("prekeys",Cs.omit(this.getPreKeys(),e)),ws.resolve()},loadSignedPreKey(e){const t=this.get("signed_prekey");return t?ws.resolve({privKey:Ts.base64ToArrayBuffer(t.privKey),pubKey:Ts.base64ToArrayBuffer(t.pubKey)}):ws.resolve()},storeSignedPreKey(e){if("object"!=typeof e)throw new Error("storeSignedPreKey: expected an object");return this.save("signed_prekey",{id:e.keyId,privKey:Ts.arrayBufferToBase64(e.keyPair.privKey),pubKey:Ts.arrayBufferToBase64(e.keyPair.pubKey),signature:Ts.arrayBufferToBase64(e.signature)}),ws.resolve()},removeSignedPreKey(e){return this.get("signed_prekey").id===e&&(this.unset("signed_prekey"),this.save()),ws.resolve()},loadSession(e){return ws.resolve(this.get("session"+e))},storeSession(e,t){return ws.resolve(this.save("session"+e,t))},removeSession(e){return ws.resolve(this.unset("session"+e))},removeAllSessions(e){const t=Cs.filter(Cs.keys(this.attributes),t=>{if(t.startsWith("session"+e))return t}),n={};return Cs.forEach(t,e=>{n[e]=void 0}),this.save(n),ws.resolve()},publishBundle(){const t=this.get("signed_prekey"),n=`${ks.NS.OMEMO_BUNDLES}:${this.get("device_id")}`,o=Ss("item").c("bundle",{xmlns:ks.NS.OMEMO}).c("signedPreKeyPublic",{signedPreKeyId:t.id}).t(t.pubKey).up().c("signedPreKeySignature").t(t.signature).up().c("identityKey").t(this.get("identity_keypair").pubKey).up().c("prekeys");Cs.forEach(this.get("prekeys"),(e,t)=>o.c("preKeyPublic",{preKeyId:t}).t(e.pubKey).up());return e.api.pubsub.publish(null,n,o,{"pubsub#access_model":"open"},!1)},async generateMissingPreKeys(){const t=this.getPreKeys(),n=Cs.difference(Cs.invokeMap(Cs.range(0,e.NUM_PREKEYS),Number.prototype.toString),Cs.keys(t));if(n.length<1)return e.log("No missing prekeys to generate for our own device",ks.LogLevel.WARN),ws.resolve();const o=await ws.all(Cs.map(n,e=>libsignal.KeyHelper.generatePreKey(parseInt(e,10))));Cs.forEach(o,e=>this.storePreKey(e.keyId,e.keyPair));const a=Cs.map(this.getPreKeys(),e=>({id:e.keyId,key:Ts.arrayBufferToBase64(e.pubKey)})),r=e.devicelists.get(e.bare_jid).devices.get(this.get("device_id")),s=await r.getBundle();r.save("bundle",Cs.extend(s,{prekeys:a}))},async generateBundle(){const t=await libsignal.KeyHelper.generateIdentityKeyPair(),n={},o=Ts.arrayBufferToBase64(t.pubKey),a=function(){const t=e.devicelists.get(e.bare_jid).devices.pluck("id");let n=libsignal.KeyHelper.generateRegistrationId(),o=0;for(;Cs.includes(t,n);)if(n=libsignal.KeyHelper.generateRegistrationId(),10==++o)throw new Error("Unable to generate a unique device ID");return n.toString()}();n.identity_key=o,n.device_id=a,this.save({device_id:a,identity_keypair:{privKey:Ts.arrayBufferToBase64(t.privKey),pubKey:o},identity_key:o});const r=await libsignal.KeyHelper.generateSignedPreKey(t,0);e.omemo_store.storeSignedPreKey(r),n.signed_prekey={id:r.keyId,public_key:Ts.arrayBufferToBase64(r.keyPair.privKey),signature:Ts.arrayBufferToBase64(r.signature)};const s=await ws.all(Cs.map(Cs.range(0,e.NUM_PREKEYS),e=>libsignal.KeyHelper.generatePreKey(e)));Cs.forEach(s,t=>e.omemo_store.storePreKey(t.keyId,t.keyPair));const c=e.devicelists.get(e.bare_jid).devices.create({id:n.device_id,jid:e.bare_jid}),f=Cs.map(s,e=>({id:e.keyId,key:Ts.arrayBufferToBase64(e.keyPair.pubKey)}));n.prekeys=f,c.save("bundle",n)},fetchSession(){return Cs.isUndefined(this._setup_promise)&&(this._setup_promise=new ws((t,n)=>{this.fetch({success:()=>{e.omemo_store.get("device_id")?t():this.generateBundle().then(t).catch(t)},error:()=>{this.generateBundle().then(t).catch(t)}})})),this._setup_promise}}),e.Device=vs.Model.extend({defaults:{trusted:0},getRandomPreKey(){const e=this.get("bundle");return e.prekeys[Ts.getRandomInt(e.prekeys.length)]},async fetchBundleFromServer(){const t=Ms({type:"get",from:e.bare_jid,to:this.get("jid")}).c("pubsub",{xmlns:ks.NS.PUBSUB}).c("items",{node:`${ks.NS.OMEMO_BUNDLES}:${this.get("id")}`});let n;try{n=await e.api.sendIQ(t)}catch(n){throw new js("Could not fetch bundle",n)}if(n.querySelector("error"))throw new js("Could not fetch bundle",n);const o=xs(`items[node="${ks.NS.OMEMO_BUNDLES}:${this.get("id")}"]`,n).pop(),a=Ns(xs(`bundle[xmlns="${ks.NS.OMEMO}"]`,o).pop());return this.save("bundle",a),a},getBundle(){return this.get("bundle")?ws.resolve(this.get("bundle"),this):this.fetchBundleFromServer()}}),e.Devices=vs.Collection.extend({model:e.Device}),e.DeviceList=vs.Model.extend({idAttribute:"jid",initialize(){this.devices=new e.Devices;const t=`converse.devicelist-${e.bare_jid}-${this.get("jid")}`,n=e.config.get("storage");this.devices.browserStorage=new vs.BrowserStorage[n](t),this.fetchDevices()},fetchDevices(){return Cs.isUndefined(this._devices_promise)&&(this._devices_promise=new ws(t=>{this.devices.fetch({success:async n=>{if(0===n.length){let n;try{n=await this.fetchDevicesFromServer()}catch(n){return e.log(`Could not fetch devices for ${this.get("jid")}`),e.log(n,ks.LogLevel.ERROR),this.destroy(),t(n)}await this.publishCurrentDevice(n)}t()},error:n=>{e.log(n,ks.LogLevel.ERROR),t(n)}})})),this._devices_promise},async publishCurrentDevice(t){if(this.get("jid")!==e.bare_jid)return;await s();let n=e.omemo_store.get("device_id");return this.devices.findWhere({id:n})||(await e.omemo_store.generateBundle(),n=e.omemo_store.get("device_id")),Cs.includes(t,n)?void 0:this.publishDevices()},async fetchDevicesFromServer(){const t=Ms({type:"get",from:e.bare_jid,to:this.get("jid")}).c("pubsub",{xmlns:ks.NS.PUBSUB}).c("items",{node:ks.NS.OMEMO_DEVICELIST});let n;try{n=await e.api.sendIQ(t)}catch(t){return e.log(t,ks.LogLevel.ERROR),[]}const o=Cs.map(xs(`list[xmlns="${ks.NS.OMEMO}"] device`,n),e=>e.getAttribute("id"));return Cs.forEach(o,e=>this.devices.create({id:e,jid:this.get("jid")})),o},publishDevices(){const t=Ss("item").c("list",{xmlns:ks.NS.OMEMO});this.devices.each(e=>t.c("device",{id:e.get("id")}).up());return e.api.pubsub.publish(null,ks.NS.OMEMO_DEVICELIST,t,{"pubsub#access_model":"open"},!1)},removeOwnDevices(t){if(this.get("jid")!==e.bare_jid)throw new Error("Cannot remove devices from someone else's device list");return Cs.forEach(t,e=>this.devices.get(e).destroy()),this.publishDevices()}}),e.DeviceLists=vs.Collection.extend({model:e.DeviceList}),e.api.waitUntil("chatBoxesInitialized").then(()=>e.chatboxes.on("add",n=>{c(n),n.get("type")===e.CHATROOMS_TYPE&&(n.occupants.on("add",o=>(async function(n,o){!o.isSelf()&&n.features.get("nonanonymous")&&n.features.get("membersonly")&&n.get("omemo_active")&&(await e.contactHasOMEMOSupport(o.get("jid"))||(n.messages.create({message:t("%1$s doesn't appear to have a client that supports OMEMO. Encrypted chat will no longer be possible in this grouchat.",o.get("nick")),type:"error"}),n.save({omemo_active:!1,omemo_supported:!1})))})(n,o)),n.features.on("change",()=>c(n)))})),e.api.listen.on("afterTearDown",()=>{e.devicelists&&e.devicelists.reset(),delete e.omemo_store}),e.api.listen.on("connected",function(){e.connection.addHandler(t=>{try{xs(`event[xmlns="${ks.NS.PUBSUB}#event"]`,t).length&&(function(t){const n=xs(`items[node="${ks.NS.OMEMO_DEVICELIST}"]`,t).pop();if(!n)return;const o=Cs.map(xs(`item list[xmlns="${ks.NS.OMEMO}"] device`,n),e=>e.getAttribute("id")),a=t.getAttribute("from"),r=(e.devicelists.get(a)||e.devicelists.create({jid:a})).devices,s=Cs.difference(r.pluck("id"),o);Cs.forEach(s,t=>{a===e.bare_jid&&t===e.omemo_store.get("device_id")||r.get(t).destroy()}),Cs.forEach(o,e=>{r.get(e)||r.create({id:e,jid:a})}),ks.getBareJidFromJid(a)===e.bare_jid&&e.devicelists.get(e.bare_jid).publishCurrentDevice(o)}(t),function(t){const n=xs("items",t).pop();if(!n||!n.getAttribute("node").startsWith(ks.NS.OMEMO_BUNDLES))return;const o=n.getAttribute("node").split(":")[1],a=t.getAttribute("from"),r=xs("item > bundle",n).pop(),s=e.devicelists.get(a)||e.devicelists.create({jid:a});(s.devices.get(o)||s.devices.create({id:o,jid:a})).save({bundle:Ns(r)})}(t))}catch(t){e.log(t.message,ks.LogLevel.ERROR)}return!0},null,"message","headline")}),e.api.listen.on("renderToolbar",e=>e.renderOMEMOToolbarButton()),e.api.listen.on("statusInitialized",async function(){if(!e.config.get("trusted"))return;e.devicelists=new e.DeviceLists;const t=e.config.get("storage"),n=`converse.devicelists-${e.bare_jid}`;e.devicelists.browserStorage=new vs.BrowserStorage[t](n),await r(),await s(),await e.omemo_store.publishBundle(),e.emit("OMEMOInitialized")}),e.api.listen.on("addClientFeatures",()=>e.api.disco.own.features.add(`${ks.NS.OMEMO_DEVICELIST}+notify`)),e.api.listen.on("userDetailsModalInitialized",t=>{const n=t.get("jid");e.generateFingerprints(n).catch(Cs.partial(e.log,Cs,ks.LogLevel.ERROR))}),e.api.listen.on("profileModalInitialized",t=>{e.generateFingerprints(e.bare_jid).catch(Cs.partial(e.log,Cs,ks.LogLevel.ERROR))}),Cs.extend(e.api,{omemo:{bundle:{generate:async()=>{const t=e.devicelists.get(e.bare_jid),o=e.omemo_store.get("device_id");if(o){const n=t.devices.get(o);e.omemo_store.unset(o),n&&await new ws(e=>n.destroy({success:e,error:e})),t.devices.trigger("remove")}return await e.omemo_store.generateBundle(),await t.publishDevices(),n(t.devices.get(e.omemo_store.get("device_id")))}}}})}});const Os=P.env,Ds=Os.Strophe,Rs=Os.$iq,Is=Os._;Ds.addNamespace("PUSH","urn:xmpp:push:0"),P.plugins.add("converse-push",{initialize(){const e=this._converse;e.__;async function t(t,n){if(!n.jid)return;if(!(await e.api.disco.supports(Ds.NS.PUSH,t||e.bare_jid)).length)return e.log(`Not disabling push app server "${n.jid}", no disco support from your server.`,Ds.LogLevel.WARN);const o=Rs({type:"set"});t!==e.bare_jid&&o.attrs({to:t}),o.c("disable",{xmlns:Ds.NS.PUSH,jid:n.jid}),n.node&&o.attrs({node:n.node}),e.api.sendIQ(o).catch(t=>{e.log(`Could not disable push app server for ${n.jid}`,Ds.LogLevel.ERROR),e.log(t,Ds.LogLevel.ERROR)})}async function n(t,n){if(!n.jid||!n.node)return;if(!await e.api.disco.getIdentity("pubsub","push",n.jid))return e.log(`Not enabling push the service "${n.jid}", it doesn't have the right disco identtiy.`,Ds.LogLevel.WARN);const o=await Promise.all([e.api.disco.supports(Ds.NS.PUSH,n.jid),e.api.disco.supports(Ds.NS.PUSH,t)]);if(!o[0].length&&!o[1].length)return e.log(`Not enabling push app server "${n.jid}", no disco support from your server.`,Ds.LogLevel.WARN);const a=Rs({type:"set"});return t!==e.bare_jid&&a.attrs({to:t}),a.c("enable",{xmlns:Ds.NS.PUSH,jid:n.jid,node:n.node}),n.secret&&a.c("x",{xmlns:Ds.NS.XFORM,type:"submit"}).c("field",{var:"FORM_TYPE"}).c("value").t(`${Ds.NS.PUBSUB}#publish-options`).up().up().c("field",{var:"secret"}).c("value").t(n.secret),e.api.sendIQ(a)}async function o(o){o=o||e.bare_jid;const a=e.session.get("push_enabled")||[];if(Is.includes(a,o))return;const r=Is.reject(e.push_app_servers,"disable"),s=Is.filter(e.push_app_servers,"disable");try{const c=Is.map(r,Is.partial(n,o)),f=Is.map(s,Is.partial(t,o));await Promise.all(c.concat(f))}catch(t){e.log("Could not enable or disable push App Server",Ds.LogLevel.ERROR),t&&e.log(t,Ds.LogLevel.ERROR)}finally{a.push(o)}e.session.save("push_enabled",a)}function a(t){t.get("type")==e.CHATROOMS_TYPE&&o(Ds.getDomainFromJid(t.get("jid")))}e.api.settings.update({push_app_servers:[],enable_muc_push:!1}),e.api.listen.on("statusInitialized",()=>o()),e.enable_muc_push&&e.api.listen.on("chatBoxesInitialized",()=>e.chatboxes.on("add",a))}});var Ps=n(45),Hs=n.n(Ps),zs=n(44),Fs=n.n(zs),Bs=n(43),qs=n.n(Bs),Ys=n(42),Us=n.n(Ys);const $s=P.env,Ws=$s.Strophe,Vs=$s.Backbone,Js=$s.sizzle,Gs=$s.$iq,Qs=$s._;Ws.addNamespace("REGISTER","jabber:iq:register");let Xs=0;Qs.each(Qs.keys(Ws.Status),function(e){Xs=Math.max(Xs,Ws.Status[e])}),Ws.Status.REGIFAIL=Xs+1,Ws.Status.REGISTERED=Xs+2,Ws.Status.CONFLICT=Xs+3,Ws.Status.NOTACCEPTABLE=Xs+5,P.plugins.add("converse-register",{overrides:{LoginPanel:{insertRegisterLink(){const e=this.__super__._converse;Qs.isUndefined(this.registerlinkview)&&(this.registerlinkview=new e.RegisterLinkView({model:this.model}),this.registerlinkview.render(),this.el.querySelector(".buttons").insertAdjacentElement("afterend",this.registerlinkview.el)),this.registerlinkview.render()},render(e){const t=this.__super__._converse;return this.__super__.render.apply(this,arguments),t.allow_registration&&!t.auto_login&&this.insertRegisterLink(),this}},ControlBoxView:{initialize(){this.__super__.initialize.apply(this,arguments),this.model.on("change:active-form",this.showLoginOrRegisterForm.bind(this))},showLoginOrRegisterForm(){this.__super__._converse;Qs.isNil(this.registerpanel)||("register"==this.model.get("active-form")?(this.loginpanel.el.classList.add("hidden"),this.registerpanel.el.classList.remove("hidden")):(this.loginpanel.el.classList.remove("hidden"),this.registerpanel.el.classList.add("hidden")))},renderRegistrationPanel(){const e=this.__super__._converse;return e.allow_registration&&(this.registerpanel=new e.RegisterPanel({model:this.model}),this.registerpanel.render(),this.registerpanel.el.classList.add("hidden"),this.el.querySelector("#converse-login-panel").insertAdjacentElement("afterend",this.registerpanel.el),this.showLoginOrRegisterForm()),this},renderLoginPanel(){return this.__super__.renderLoginPanel.apply(this,arguments),this.renderRegistrationPanel(),this}}},initialize(){const e=this._converse,t=e.__;function n(t){e.api.waitUntil("controlboxInitialized").then(()=>{e.chatboxes.get("controlbox").set({"active-form":t})}).catch(Qs.partial(e.log,Qs,Ws.LogLevel.FATAL))}e.CONNECTION_STATUS[Ws.Status.REGIFAIL]="REGIFAIL",e.CONNECTION_STATUS[Ws.Status.REGISTERED]="REGISTERED",e.CONNECTION_STATUS[Ws.Status.CONFLICT]="CONFLICT",e.CONNECTION_STATUS[Ws.Status.NOTACCEPTABLE]="NOTACCEPTABLE",e.api.settings.update({allow_registration:!0,domain_placeholder:t(" e.g. conversejs.org"),providers_link:"https://compliance.conversations.im/",registration_domain:""}),e.router.route("converse/login",Qs.partial(n,"login")),e.router.route("converse/register",Qs.partial(n,"register")),e.RegisterLinkView=Vs.VDOMView.extend({toHTML(){return Hs()(Qs.extend(this.model.toJSON(),{__:e.__,_converse:e,connection_status:e.connfeedback.get("connection_status")}))}}),e.RegisterPanel=Vs.NativeView.extend({tagName:"div",id:"converse-register-panel",className:"controlbox-pane fade-in",events:{"submit form#converse-register":"onFormSubmission","click .button-cancel":"renderProviderChoiceForm"},initialize(e){this.reset(),this.registerHooks()},render(){return this.model.set("registration_form_rendered",!1),this.el.innerHTML=Fs()({__:t,default_domain:e.registration_domain,label_register:t("Fetch registration form"),help_providers:t("Tip: A list of public XMPP providers is available"),help_providers_link:t("here"),href_providers:e.providers_link,domain_placeholder:e.domain_placeholder}),e.registration_domain&&this.fetchRegistrationForm(e.registration_domain),this},registerHooks(){const t=e.connection,n=t._connect_cb.bind(t);t._connect_cb=((e,t,o)=>{this._registering?this.getRegistrationFields(e,t,o)&&(this._registering=!1):n(e,t,o)})},getRegistrationFields(n,o,a){const r=e.connection;r.connected=!0;const s=r._proto._reqToData(n);if(!s)return;if(r._proto._connect_cb(s)===Ws.Status.CONNFAIL)return this.showValidationError(t("Sorry, we're unable to connect to your chosen provider.")),!1;const c=s.getElementsByTagName("register"),f=s.getElementsByTagName("mechanism");if(0===c.length&&0===f.length)return r._proto._no_auth_received(o),!1;if(0===c.length)return r._changeConnectStatus(Ws.Status.REGIFAIL),this.showValidationError(t("Sorry, the given provider does not support in band account registration. Please try with a different provider.")),!0;r._addSysHandler(this.onRegistrationFields.bind(this),null,"iq",null,null);const i=Gs({type:"get"}).c("query",{xmlns:Ws.NS.REGISTER}).tree();return i.setAttribute("id",r.getUniqueId("sendIQ")),r.send(i),r.connected=!1,!0},onRegistrationFields(n){return"error"===n.getAttribute("type")?(e.connection._changeConnectStatus(Ws.Status.REGIFAIL,t('Something went wrong while establishing a connection with "%1$s". Are you sure it exists?',this.domain)),!1):1!==n.getElementsByTagName("query").length?(e.connection._changeConnectStatus(Ws.Status.REGIFAIL,"unknown"),!1):(this.setFields(n),this.model.get("registration_form_rendered")||this.renderRegistrationForm(n),!1)},reset(e){const t={fields:{},urls:[],title:"",instructions:"",registered:!1,_registering:!1,domain:null,form_type:null};Qs.extend(this,t),e&&Qs.extend(this,Qs.pick(e,Qs.keys(t)))},onFormSubmission(e){e&&e.preventDefault&&e.preventDefault(),Qs.isNull(e.target.querySelector("input[name=domain]"))?this.submitRegistrationForm(e.target):this.onProviderChosen(e.target)},onProviderChosen(e){const t=e.querySelector("input[name=domain]"),n=Qs.get(t,"value");n?(e.querySelector("input[type=submit]").classList.add("hidden"),this.fetchRegistrationForm(n.trim())):t.classList.add("error")},fetchRegistrationForm(t){return this.model.get("registration_form_rendered")||this.renderRegistrationRequest(),this.reset({domain:Ws.getDomainFromJid(t),_registering:!0}),e.connection.connect(this.domain,"",this.onConnectStatusChanged.bind(this)),!1},renderRegistrationRequest(){this.clearRegistrationForm().insertAdjacentHTML("beforeend",Us()({__:e.__,cancel:e.registration_domain}))},giveFeedback(e,t){let n=this.el.querySelector(".reg-feedback");Qs.isNull(n)||n.parentNode.removeChild(n);const o=this.el.querySelector("form");o.insertAdjacentHTML("afterbegin",''),(n=o.querySelector(".reg-feedback")).textContent=e,t&&n.classList.add(t)},clearRegistrationForm(){const e=this.el.querySelector("form");return e.innerHTML="",this.model.set("registration_form_rendered",!1),e},showSpinner(){return this.el.querySelector("form").innerHTML=uo()(),this.model.set("registration_form_rendered",!1),this},onConnectStatusChanged(n){e.log("converse-register: onConnectStatusChanged"),Qs.includes([Ws.Status.DISCONNECTED,Ws.Status.CONNFAIL,Ws.Status.REGIFAIL,Ws.Status.NOTACCEPTABLE,Ws.Status.CONFLICT],n)?(e.log(`Problem during registration: Strophe.Status is ${e.CONNECTION_STATUS[n]}`,Ws.LogLevel.ERROR),this.abortRegistration()):n===Ws.Status.REGISTERED&&(e.log("Registered successfully."),e.connection.reset(),this.showSpinner(),Qs.includes(["converse/login","converse/register"],Vs.history.getFragment())&&e.router.navigate("",{replace:!0}),this.fields.password&&this.fields.username?(e.connection.connect(this.fields.username.toLowerCase()+"@"+this.domain.toLowerCase(),this.fields.password,e.onConnectStatusChanged),this.giveFeedback(t("Now logging you in"),"info")):(e.chatboxviews.get("controlbox").renderLoginPanel(),e.giveFeedback(t("Registered successfully"))),this.reset())},renderLegacyRegistrationForm(e){Qs.each(Qs.keys(this.fields),t=>{"username"===t?e.insertAdjacentHTML("beforeend",ln()({domain:` @${this.domain}`,name:t,type:"text",label:t,value:"",required:!0})):e.insertAdjacentHTML("beforeend",an()({label:t,name:t,placeholder:t,required:!0,type:"password"===t||"email"===t?t:"text",value:""}))}),Qs.each(this.urls,t=>{e.insertAdjacentHTML("afterend",''+t+"")})},renderRegistrationForm(t){const n=this.el.querySelector("form");n.innerHTML=qs()({__:e.__,domain:this.domain,title:this.title,instructions:this.instructions,registration_domain:e.registration_domain});const o=n.querySelector("fieldset.buttons");"xform"===this.form_type?Qs.each(t.querySelectorAll("field"),e=>{o.insertAdjacentHTML("beforebegin",He.xForm2webForm(e,t,this.domain))}):this.renderLegacyRegistrationForm(n),this.fields||n.querySelector(".button-primary").classList.add("hidden"),n.classList.remove("hidden"),this.model.set("registration_form_rendered",!0)},showValidationError(e){const t=this.el.querySelector("form");let n=t.querySelector(".form-errors");if(Qs.isNull(n)){n='';const e=t.querySelector("p.instructions");Qs.isNull(e)?t.insertAdjacentHTML("afterbegin",n):e.insertAdjacentHTML("afterend",n),n=t.querySelector(".form-errors")}else n.innerHTML="";n.insertAdjacentHTML("beforeend",''+e+"
"),n.classList.remove("hidden")},reportErrors(e){const n=e.querySelectorAll("error");if(Qs.each(n,e=>{this.showValidationError(e.textContent)}),!n.length){const e=t("The provider rejected your registration attempt. Please check the values you entered for correctness.");this.showValidationError(e)}},renderProviderChoiceForm(t){t&&t.preventDefault&&t.preventDefault(),e.connection._proto._abortAllRequests(),e.connection.reset(),this.render()},abortRegistration(){e.connection._proto._abortAllRequests(),e.connection.reset(),this.model.get("registration_form_rendered")?e.registration_domain&&this.model.get("registration_form_rendered")&&this.fetchRegistrationForm(e.registration_domain):this.render()},submitRegistrationForm(t){if(Qs.reduce(this.el.querySelectorAll("input.required"),function(e,t){return""===t.value?(t.classList.add("error"),e+1):e},0))return;const n=Js(":input:not([type=button]):not([type=submit])",t),o=Gs({type:"set",id:e.connection.getUniqueId()}).c("query",{xmlns:Ws.NS.REGISTER});"xform"===this.form_type?(o.c("x",{xmlns:Ws.NS.XFORM,type:"submit"}),Qs.each(n,e=>{o.cnode(He.webForm2xForm(e)).up()})):Qs.each(n,e=>{o.c(e.getAttribute("name"),{},e.value)}),e.connection._addSysHandler(this._onRegisterIQ.bind(this),null,"iq",null,null),e.connection.send(o),this.setFields(o.tree())},setFields(e){const t=e.querySelector("query"),n=Js(`x[xmlns="${Ws.NS.XFORM}"]`,t);n.length>0?this._setFieldsFromXForm(n.pop()):this._setFieldsFromLegacy(t)},_setFieldsFromLegacy(e){Qs.each(e.children,e=>{"instructions"!==e.tagName.toLowerCase()?"x"!==e.tagName.toLowerCase()?this.fields[e.tagName.toLowerCase()]=Ws.getText(e):"jabber:x:oob"===e.getAttribute("xmlns")&&this.urls.concat(Qs.map(e.querySelectorAll("url"),"textContent")):this.instructions=Ws.getText(e)}),this.form_type="legacy"},_setFieldsFromXForm(t){this.title=Qs.get(t.querySelector("title"),"textContent"),this.instructions=Qs.get(t.querySelector("instructions"),"textContent"),Qs.each(t.querySelectorAll("field"),t=>{const n=t.getAttribute("var");n?this.fields[n.toLowerCase()]=Qs.get(t.querySelector("value"),"textContent",""):e.log("Found field we couldn't parse",Ws.LogLevel.WARN)}),this.form_type="xform"},_onRegisterIQ(t){if("error"===t.getAttribute("type")){e.log("Registration failed.",Ws.LogLevel.ERROR),this.reportErrors(t);let n=t.getElementsByTagName("error");if(1!==n.length)return e.connection._changeConnectStatus(Ws.Status.REGIFAIL,"unknown"),!1;"conflict"===(n=n[0].firstChild.tagName.toLowerCase())?e.connection._changeConnectStatus(Ws.Status.CONFLICT,n):"not-acceptable"===n?e.connection._changeConnectStatus(Ws.Status.NOTACCEPTABLE,n):e.connection._changeConnectStatus(Ws.Status.REGIFAIL,n)}else e.connection._changeConnectStatus(Ws.Status.REGISTERED,null);return!1}})}});var Ks=n(41),Zs=n.n(Ks),ec=n(40),tc=n.n(ec);const nc=P.env,oc=nc.Backbone,ac=nc.Promise,rc=nc.Strophe,sc=(nc.sizzle,nc._),cc=P.env.utils;P.plugins.add("converse-roomslist",{dependencies:["converse-singleton","converse-controlbox","converse-muc","converse-bookmarks"],initialize(){const e=this._converse,t=e.__;e.api.promises.add("roomsListInitialized"),e.OpenRooms=oc.Collection.extend({comparator(t){if(t.get("bookmarked")){return sc.head(e.bookmarksview.model.where({jid:t.get("jid")})).get("name")}return t.get("name")},initialize(){e.chatboxes.on("add",this.onChatBoxAdded,this),e.chatboxes.on("change:hidden",this.onChatBoxChanged,this),e.chatboxes.on("change:bookmarked",this.onChatBoxChanged,this),e.chatboxes.on("change:name",this.onChatBoxChanged,this),e.chatboxes.on("change:num_unread",this.onChatBoxChanged,this),e.chatboxes.on("change:num_unread_general",this.onChatBoxChanged,this),e.chatboxes.on("remove",this.onChatBoxRemoved,this),this.reset(sc.map(e.chatboxes.where({type:"chatroom"}),"attributes"))},onChatBoxAdded(e){"chatroom"===e.get("type")&&this.create(e.attributes)},onChatBoxChanged(e){if("chatroom"===e.get("type")){const t=this.get(e.get("jid"));sc.isNil(t)||t.set(e.attributes)}},onChatBoxRemoved(e){if("chatroom"===e.get("type")){const t=this.get(e.get("jid"));this.remove(t)}}}),e.RoomsList=oc.Model.extend({defaults:{"toggle-state":e.OPENED}}),e.RoomsListElementView=oc.VDOMView.extend({events:{"click .room-info":"showRoomDetailsModal"},initialize(){this.model.on("destroy",this.remove,this),this.model.on("remove",this.remove,this),this.model.on("change:bookmarked",this.render,this),this.model.on("change:hidden",this.render,this),this.model.on("change:name",this.render,this),this.model.on("change:num_unread",this.render,this),this.model.on("change:num_unread_general",this.render,this)},toHTML(){return tc()(sc.extend(this.model.toJSON(),{allow_bookmarks:e.allow_bookmarks&&e.bookmarks,currently_open:e.isUniView()&&!this.model.get("hidden"),info_leave_room:t("Leave this groupchat"),info_remove_bookmark:t("Unbookmark this groupchat"),info_add_bookmark:t("Bookmark this groupchat"),info_title:t("Show more information on this groupchat"),name:this.getRoomsListElementName(),open_title:t("Click to open this groupchat")}))},showRoomDetailsModal(t){const n=e.chatboxes.get(this.model.get("jid"));t.preventDefault(),sc.isUndefined(n.room_details_modal)&&(n.room_details_modal=new e.RoomDetailsModal({model:n})),n.room_details_modal.show(t)},getRoomsListElementName(){if(this.model.get("bookmarked")&&e.bookmarksview){return sc.head(e.bookmarksview.model.where({jid:this.model.get("jid")})).get("name")}return this.model.get("name")}}),e.RoomsListView=oc.OrderedListView.extend({tagName:"div",className:"open-rooms-list list-container rooms-list-container",events:{"click .add-bookmark":"addBookmark","click .close-room":"closeRoom","click .list-toggle":"toggleRoomsList","click .remove-bookmark":"removeBookmark","click .open-room":"openRoom"},listSelector:".rooms-list",ItemView:e.RoomsListElementView,subviewIndex:"jid",initialize(){oc.OrderedListView.prototype.initialize.apply(this,arguments),this.model.on("add",this.showOrHide,this),this.model.on("remove",this.showOrHide,this);const t=e.config.get("storage"),n=`converse.roomslist${e.bare_jid}`;this.list_model=new e.RoomsList({id:n}),this.list_model.browserStorage=new oc.BrowserStorage[t](n),this.list_model.fetch(),this.render(),this.sortAndPositionAllItems()},render(){return this.el.innerHTML=Zs()({toggle_state:this.list_model.get("toggle-state"),desc_rooms:t("Click to toggle the list of open groupchats"),label_rooms:t("Open Groupchats"),_converse:e}),this.list_model.get("toggle-state")!==e.OPENED&&this.el.querySelector(".open-rooms-list").classList.add("collapsed"),this.showOrHide(),this.insertIntoControlBox(),this},insertIntoControlBox(){const t=e.chatboxviews.get("controlbox");if(!sc.isUndefined(t)&&!cc.rootContains(e.root,this.el)){const e=t.el.querySelector(".open-rooms-list");sc.isNull(e)||e.parentNode.replaceChild(this.el,e)}},hide(){cc.hideElement(this.el)},show(){cc.showElement(this.el)},async openRoom(t){t.preventDefault();const n=t.target.textContent,o=t.target.getAttribute("data-room-jid"),a={name:n||rc.unescapeNode(rc.getNodeFromJid(o))||o};await e.api.rooms.open(o,a),e.api.chatviews.get(o).focus()},closeRoom(n){n.preventDefault();const o=n.target.getAttribute("data-room-name"),a=n.target.getAttribute("data-room-jid");confirm(t("Are you sure you want to leave the groupchat %1$s?",o))&&e.chatboxviews.get(a).close()},showOrHide(e){this.model.models.length?cc.showElement(this.el):cc.hideElement(this.el)},removeBookmark:e.removeBookmarkViaEvent,addBookmark:e.addBookmarkViaEvent,toggleRoomsList(t){t&&t.preventDefault&&t.preventDefault();const n=t.target.matches(".fa")?t.target:t.target.querySelector(".fa");n.classList.contains("fa-caret-down")?cc.slideIn(this.el.querySelector(".open-rooms-list")).then(()=>{this.list_model.save({"toggle-state":e.CLOSED}),n.classList.remove("fa-caret-down"),n.classList.add("fa-caret-right")}):cc.slideOut(this.el.querySelector(".open-rooms-list")).then(()=>{this.list_model.save({"toggle-state":e.OPENED}),n.classList.remove("fa-caret-right"),n.classList.add("fa-caret-down")})}});const n=function(){const t=e.config.get("storage"),n=new e.OpenRooms;n.browserStorage=new oc.BrowserStorage[t]("converse.open-rooms-{_converse.bare_jid}"),e.rooms_list_view=new e.RoomsListView({model:n}),e.api.emit("roomsListInitialized")};e.on("connected",async()=>{e.allow_bookmarks?await e.api.waitUntil("bookmarksInitialized"):await ac.all([e.api.waitUntil("chatBoxesFetched"),e.api.waitUntil("roomsPanelRendered")]),n()}),e.api.listen.on("reconnected",n)}});const fc=["converse-autocomplete","converse-bookmarks","converse-caps","converse-chatboxviews","converse-chatview","converse-controlbox","converse-dragresize","converse-embedded","converse-fullscreen","converse-headline","converse-message-view","converse-minimize","converse-modal","converse-muc-views","converse-notification","converse-oauth","converse-omemo","converse-profile","converse-push","converse-register","converse-roomslist","converse-rosterview","converse-singleton"],ic=P.initialize;P.initialize=function(e,t){return P.env._.isArray(e.whitelisted_plugins)?e.whitelisted_plugins=e.whitelisted_plugins.concat(fc):e.whitelisted_plugins=fc,ic(e,t)};t.default=P},function(e,t,n){var o={escape:n(1)};e.exports=function(e){var t="",n=o.escape;return t+='\x3c!-- src/templates/search_contact.html --\x3e\n