From b23712aec3eaba80595853476cb15079e9dbaef1 Mon Sep 17 00:00:00 2001 From: worlword Date: Tue, 3 Apr 2018 11:20:57 +0200 Subject: [PATCH 01/12] added httpFileUpload to converse.js --- locale/de/LC_MESSAGES/converse.po | 4 + src/build.js | 1 + src/config.js | 1 + src/converse-chatview.js | 70 ++++++-- src/converse-core.js | 1 + src/converse-httpFileUpload.js | 269 ++++++++++++++++++++++++++++ src/converse-muc-views.js | 18 +- src/converse.js | 1 + src/inverse.js | 1 + src/templates/chatroom_toolbar.html | 6 + src/templates/toolbar.html | 6 + src/utils/core.js | 8 + tests/runner-transpiled.js | 1 + 13 files changed, 376 insertions(+), 11 deletions(-) create mode 100644 src/converse-httpFileUpload.js diff --git a/locale/de/LC_MESSAGES/converse.po b/locale/de/LC_MESSAGES/converse.po index b30fd193b..6642b5366 100644 --- a/locale/de/LC_MESSAGES/converse.po +++ b/locale/de/LC_MESSAGES/converse.po @@ -145,6 +145,10 @@ msgstr "tippt nicht mehr" msgid "has gone away" msgstr "ist jetzt abwesend" +#: src/converse-chatview.js:860 +msgid "Upload a File" +msgstr "Datei versenden" + #: dist/converse-no-dependencies.js:14851 #: dist/converse-no-dependencies.js:23427 msgid "Remove messages" diff --git a/src/build.js b/src/build.js index b3ba27873..a2a1f3b4f 100644 --- a/src/build.js +++ b/src/build.js @@ -13,6 +13,7 @@ "converse-dragresize": "builds/converse-dragresize", "converse-fullscreen": "builds/converse-fullscreen", "converse-headline": "builds/converse-headline", + "converse-httpFileUpload": "builds/converse-httpFileUpload", "converse-mam": "builds/converse-mam", "converse-minimize": "builds/converse-minimize", "converse-modal": "builds/converse-modal", diff --git a/src/config.js b/src/config.js index ef89f5cc0..96758d97e 100644 --- a/src/config.js +++ b/src/config.js @@ -74,6 +74,7 @@ require.config({ "converse-dragresize": "src/converse-dragresize", "converse-fullscreen": "src/converse-fullscreen", "converse-headline": "src/converse-headline", + "converse-httpFileUpload": "src/converse-httpFileUpload", "converse-mam": "src/converse-mam", "converse-minimize": "src/converse-minimize", "converse-modal": "src/converse-modal", diff --git a/src/converse-chatview.js b/src/converse-chatview.js index 27dd4cbb9..f7e36e12f 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -25,6 +25,7 @@ "tpl!spoiler_button", "tpl!spoiler_message", "tpl!toolbar", + "converse-httpFileUpload", "converse-chatboxes" ], factory); }(this, function ( @@ -44,7 +45,8 @@ tpl_spinner, tpl_spoiler_button, tpl_spoiler_message, - tpl_toolbar + tpl_toolbar, + filetransfer ) { "use strict"; const { $msg, Backbone, Promise, Strophe, _, b64_sha1, f, sizzle, moment } = converse.env; @@ -54,6 +56,8 @@ FORWARD_SLASH: 47 }; + Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob'); + converse.plugins.add('converse-chatview', { /* Plugin dependencies are other plugins which might be * overridden or relied upon, and therefore need to be loaded before @@ -108,7 +112,8 @@ 'call': false, 'clear': true, 'emoji': true, - 'spoiler': true + 'spoiler': true, + 'fileUpload': true }, }); emojione.imagePathPNG = _converse.emojione_image_path; @@ -247,7 +252,22 @@ 'click .toggle-smiley': 'toggleEmojiMenu', 'click .toggle-spoiler': 'toggleSpoilerMessage', 'click .toggle-compose-spoiler': 'toggleComposeSpoilerMessage', - 'keypress .chat-textarea': 'keyPressed' + 'keypress .chat-textarea': 'keyPressed', + 'click .toggle-fileUpload': 'toggleFileUpload', + 'change .fileUpload_input': 'handleFileSelect' + }, + + toggleFileUpload(ev) { + _converse.FileUpload.prototype.initFiletransfer(_converse.connection); + var uploadDialog = this.el.querySelector('.fileUpload_input'); + uploadDialog.click(); + }, + + handleFileSelect(evt) { + var files = evt.target.files; + var file = files[0]; + var jid = this.jid; + _converse.FileUpload.prototype.setFile(file,this); }, initialize () { @@ -368,9 +388,11 @@ 'label_clear': __('Clear all messages'), 'label_insert_smiley': __('Insert a smiley'), 'label_start_call': __('Start a call'), + 'label_upload_file': __('Upload a File'), 'label_toggle_spoiler': label_toggle_spoiler, 'show_call_button': _converse.visible_toolbar_buttons.call, 'show_spoiler_button': _converse.visible_toolbar_buttons.spoiler, + 'show_fileUpload_button': _converse.visible_toolbar_buttons.fileUpload, 'use_emoji': _converse.visible_toolbar_buttons.emoji, }); }, @@ -639,7 +661,14 @@ if (attrs.is_spoiler) { this.renderSpoilerMessage(msg, attrs) } - u.renderImageURLs(msg_content).then(this.scrollDown.bind(this)); + + if(msg_content.textContent.endsWith('mp4')){ + msg_content.innerHTML = u.renderMovieURLs(msg_content); + } else if(msg_content.textContent.endsWith('mp3')){ + msg_content.innerHTML = u.renderAudioURLs(msg_content); + } else { + u.renderImageURLs(msg_content).then(this.scrollDown.bind(this)); + } return msg; }, @@ -771,6 +800,19 @@ return stanza; }, + createFileMessageStanza(message){ + const stanza = $msg({ + 'from': _converse.connection.jid, + 'to': this.model.get('jid'), + 'type': 'chat', + 'id': message.get('msgid') + }).c('body').t(message.get('message')).up() + .c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up() + .c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('message')).up(); + + return stanza; + }, + sendMessage (message) { /* Responsible for sending off a text message. * @@ -779,8 +821,7 @@ */ // TODO: We might want to send to specfic resources. // Especially in the OTR case. - const messageStanza = this.createMessageStanza(message); - _converse.connection.send(messageStanza); + _converse.connection.send(message); if (_converse.forward_messages) { // Forward the message, so that other connected resources are also aware of it. _converse.connection.send( @@ -790,7 +831,7 @@ 'xmns': Strophe.NS.DELAY, 'stamp': moment().format() }).up() - .cnode(messageStanza.tree()) + .cnode(message.tree()) ); } }, @@ -814,7 +855,7 @@ } }, - onMessageSubmitted (text, spoiler_hint) { + onMessageSubmitted (text, spoiler_hint, file = null) { /* This method gets called once the user has typed a message * and then pressed enter in a chat box. * @@ -833,9 +874,18 @@ if (this.parseMessageForCommands(text)) { return; } - const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint) + const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint); const message = this.model.messages.create(attrs); - this.sendMessage(message); + + /* check, if a file was send. If true it will send the file with XEP-0066. */ + var messageStanza; + if(file !== null){ + messageStanza = this.createFileMessageStanza(message); + } + else { + messageStanza = this.createMessageStanza(message); + } + this.sendMessage(messageStanza); }, getOutgoingMessageAttributes (text, spoiler_hint) { diff --git a/src/converse-core.js b/src/converse-core.js index 2c738c2b1..a3a95a358 100644 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -78,6 +78,7 @@ 'converse-dropdown', 'converse-fullscreen', 'converse-headline', + 'converse-httpFileUpload', 'converse-mam', 'converse-minimize', 'converse-modal', diff --git a/src/converse-httpFileUpload.js b/src/converse-httpFileUpload.js new file mode 100644 index 000000000..dfc4b3a01 --- /dev/null +++ b/src/converse-httpFileUpload.js @@ -0,0 +1,269 @@ +/* + The MIT License (MIT) + + Copyright (c) 2014 Klaus Herberth + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/** + * Implements Http File Upload (XEP-0363) + * + * @see {@link http://xmpp.org/extensions/xep-0363.html} + */ +(function (root, factory) { + define([ + "converse-core", + ], factory); +}(this, function ( + converse + ) { + "use strict"; + const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env; + + Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload'); + + var requestSlotUrl; + var ready; + var httpUploadOption = { + enable: true + } + + converse.plugins.add('converse-httpFileUpload', { + + dependencies: ["converse-chatboxes", "converse-disco"], + + initialize() { + const { _converse } = this, + { __ } = _converse; + var connection = _converse.connection; + var domain; + var file; + var chatBox; + + _converse.FileUpload = Backbone.NativeView.extend({ + /** + * Set up http file upload. + * + * @param {*} connection the current strophe-connection + */ + initFiletransfer () { + connection = _converse.connection; + domain = _converse.connection.domain; + + if (httpUploadOption && requestSlotUrl != undefined) { + ready = true; + return; + } + this.discoverUploadService(); + }, + + /** + * Discover upload service for http upload. + * + */ + discoverUploadService () { + var self = this; + console.log('discover http upload service'); + connection.disco.items(domain, null, function(items) { + var childs = items.getElementsByTagName('item'); + for(var i = 0; i < childs.length; i++){ + var jid = childs[i].attributes.jid.value; + if (ready) { + // abort, because we already found a service + return false; + } + self.queryItemForUploadService(jid); + } + }); + }, + + /** + * Query item for upload service. + * + * @param {String} jid of the logged-in user + * @param {Function} cb Callback on success + */ + queryItemForUploadService (jid) { + var self = this; + console.log('query ' + jid + ' for upload service'); + + connection.disco.info(jid, null, function(info) { + var httpUploadFeature; + var temp = info.getElementsByTagName('feature'); + for(var i = 0; i < temp.length; i++){ + var feature = temp[i].attributes.var; + if(feature != undefined && feature.value === Strophe.NS.HTTPUPLOAD){ + requestSlotUrl = jid; + ready = true; + self.sendFile(); + } + } + }); + }, + + /** + * Saves the file the user has picked. + * + * @param {*} file the name of the file the user has picked. + * @param {*} chatBox the chatbox from which the user initiated the file-upload + */ + setFile (file1, chatBox1){ + file = file1; + chatBox = chatBox1; + this.sendFile(); + }, + + /** + * Upload file. + * Waits till the Upload-Service is discovered and till the user has picked a file. + * + */ + sendFile () { + var self = this; + if(file === undefined){ + console.log("waiting to choose a file"); + return; + } + else if(requestSlotUrl === undefined){ + console.log("waiting for service discovery"); + return; + } + + console.log('Send file via http upload'); + chatBox.showHelpMessages([__('The file upload starts now')],'info'); + this.requestSlot(file, function(data) { + if (!data) { + // general error + console.log('Unknown error while requesting upload slot.'); + alert(__('File upload failed. Please check the log.')); + } else if (data.error) { + // specific error + console.log('The XMPP-Server return an error of the type: ' + data.error.type); + alert(__('File upload failed. Please check the log.')); + } else if (data.get && data.put) { + console.log('slot received, start upload to ' + data.put); + self.uploadFile(data.put, file, function() { + console.log(data.put); + + chatBox.onMessageSubmitted(data.put, null, file); + file = undefined; + }); + } + }); + }, + + /** + * Request upload slot from xmpp-server + * + * @param {File} file the file the user picked + * @param {Function} cb Callback after finished request + */ + requestSlot (file, cb) { + var self = this; + console.log("try sending file to: " + requestSlotUrl); + var iq = converse.env.$iq({ + to: requestSlotUrl, + type: 'get' + }).c('request', { + xmlns: Strophe.NS.HTTPUPLOAD + }).c('filename').t(file.name) + .up() + .c('size').t(file.size); + + connection.sendIQ(iq, function(stanza) { + self.successfulRequestSlotCB(stanza, cb); + }, function(stanza) { + self.failedRequestSlotCB(stanza, cb); + }); + }, + + /** + * Upload the given file to the given url. + * + * @param {String} url upload url + * @param {File} file the file the user picked + * @param {Function} success_cb callback on successful transition + */ + uploadFile (url, file, success_cb) { + console.log("uploadFile start"); + var xmlhttp = new XMLHttpRequest(); + var type = 'PUT'; + var contentType = 'application/octet-stream'; + var data = file; + var processData = false; + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == XMLHttpRequest.DONE) { + console.log("Status: " + xmlhttp.status); + if (xmlhttp.status == 200 || xmlhttp.status == 201) { + console.log('file successful uploaded'); + if (success_cb) { + success_cb(); + } + } + else { + console.log('error while uploading file to ' + url); + alert(__('Could not upload File please try again.')); + } + } + }; + + xmlhttp.open(type, url, true); + xmlhttp.setRequestHeader("Content-type", contentType); + xmlhttp.send(data); + + console.log("uploadFile end"); + }, + + /** + * Process successful response to slot request. + * + * @param {String} stanza + * @param {Function} cb + */ + successfulRequestSlotCB (stanza, cb) { + var slot = stanza.getElementsByTagName('slot')[0]; + + if (slot != undefined) { + var put = slot.getElementsByTagName('put')[0].textContent; + var get = slot.getElementsByTagName('get')[0].textContent; + cb({ + put: put, + get: get + }); + } else { + this.failedRequestSlotCB(stanza, cb); + } + }, + + /** + * Process failed response to slot request. + * + * @param {String} stanza + * @param {Function} cb + */ + failedRequestSlotCB (stanza, cb) { + chatBox.showHelpMessages([__('Fileupload failed')],'info'); + } + }) + } + }); + + return converse; +})); \ No newline at end of file diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index a1cba9f7b..9487b6222 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -82,6 +82,8 @@ 'unmoderated': 'moderated' }; + Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob'); + converse.plugins.add('converse-muc-views', { /* Dependencies are other plugins which might be * overridden or relied upon, and therefore need to be loaded before @@ -693,6 +695,17 @@ msgid }); }, + sendChatRoomFile (text) { + const msgid = _converse.connection.getUniqueId(); + const stanza = $msg({ + 'from': _converse.connection.jid, + 'to': this.model.get('jid'), + 'type': 'groupchat', + 'id': msgid + }).c("body").t(text).up() + .c("x", {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(text).up(); + _converse.connection.send(stanza); + }, modifyRole(room, nick, role, reason, onSuccess, onError) { const item = $build("item", {nick, role}); @@ -732,13 +745,16 @@ this.showStatusNotification(__("Error: could not execute the command"), true); }, - onMessageSubmitted (text) { + onMessageSubmitted (text, notNeeded, file = null) { /* Gets called when the user presses enter to send off a * message in a chat room. * * Parameters: * (String) text - The message text. */ + if(file !== null){ + return this.sendChatRoomFile(text); + } if (_converse.muc_disable_moderator_commands) { return this.sendChatRoomMessage(text); } diff --git a/src/converse.js b/src/converse.js index 1e918f763..6c8a48ee2 100644 --- a/src/converse.js +++ b/src/converse.js @@ -24,6 +24,7 @@ if (typeof define !== 'undefined') { "converse-minimize", // Allows chat boxes to be minimized "converse-dragresize", // Allows chat boxes to be resized by dragging them "converse-headline", // Support for headline messages + "converse-httpFileUpload", // Support for XEP-0363 "converse-fullscreen" /* END: Removable components */ ], function (converse) { diff --git a/src/inverse.js b/src/inverse.js index 14d5c3ff0..6ead412e9 100644 --- a/src/inverse.js +++ b/src/inverse.js @@ -25,6 +25,7 @@ if (typeof define !== 'undefined') { "converse-register", // XEP-0077 In-band registration "converse-roomslist", // Show currently open chat rooms "converse-vcard", // XEP-0054 VCard-temp + "converse-httpFileUpload", // Support for XEP-0363 /* END: Removable components */ "converse-inverse", // Inverse plugin for converse.js diff --git a/src/templates/chatroom_toolbar.html b/src/templates/chatroom_toolbar.html index cc3b83b3d..62704930b 100644 --- a/src/templates/chatroom_toolbar.html +++ b/src/templates/chatroom_toolbar.html @@ -4,6 +4,12 @@ {[ } ]} +{[ if (o.show_fileUpload_button) { ]} + +
  • + +
  • +{[ } ]} {[ if (o.show_call_button) { ]}
  • {[ } ]} diff --git a/src/templates/toolbar.html b/src/templates/toolbar.html index 3dca527ff..d30008a23 100644 --- a/src/templates/toolbar.html +++ b/src/templates/toolbar.html @@ -3,6 +3,12 @@ {[ } ]} +{[ if (o.show_fileUpload_button) { ]} + +
  • + +
  • +{[ } ]} {[ if (o.show_call_button) { ]}
  • {[ } ]} diff --git a/src/utils/core.js b/src/utils/core.js index f5acbf40a..173666b90 100644 --- a/src/utils/core.js +++ b/src/utils/core.js @@ -213,6 +213,14 @@ )) }; + u.renderMovieURLs = function (obj) { + return ""; + }; + + u.renderAudioURLs = function (obj) { + return ""; + }; + u.slideInAllElements = function (elements, duration=300) { return Promise.all( _.map( diff --git a/tests/runner-transpiled.js b/tests/runner-transpiled.js index 97564fef1..06d4cb0fa 100644 --- a/tests/runner-transpiled.js +++ b/tests/runner-transpiled.js @@ -24,6 +24,7 @@ config.paths["converse-core"] = "builds/converse-core"; config.paths["converse-disco"] = "builds/converse-disco"; config.paths["converse-dragresize"] = "builds/converse-dragresize"; config.paths["converse-headline"] = "builds/converse-headline"; +config.paths["converse-httpFileUpload"]="builds/converse-httpFileUpload"; config.paths["converse-fullscreen"] = "builds/converse-fullscreen"; config.paths["converse-mam"] = "builds/converse-mam"; config.paths["converse-minimize"] = "builds/converse-minimize"; From 530e7f9d3f24af7d8faf902400c2bbbe55ed7b06 Mon Sep 17 00:00:00 2001 From: worlword Date: Tue, 3 Apr 2018 12:29:12 +0200 Subject: [PATCH 02/12] fixed file-upload-button in MUC and added changes.md entry --- CHANGES.md | 7 + dist/converse.js | 1490 ++++++++++++++++++++++++------------- src/converse-muc-views.js | 2 + 3 files changed, 961 insertions(+), 538 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5f5a9a1e1..150ddd72e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changelog +## Http-File-Upload + +## New Features + +- files can now be send via http-file-upload (XEP-0363) +- mp4 and mp3 files will now be playable directly in chat. + ## 4.0.0 (Unreleased) ## UI changes diff --git a/dist/converse.js b/dist/converse.js index 26213aa55..9749da2ad 100644 --- a/dist/converse.js +++ b/dist/converse.js @@ -28230,6 +28230,73 @@ return es; }))); +//! moment.js locale configuration +//! locale : Basque [eu] +//! author : Eneko Illarramendi : https://github.com/eillarra + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define('moment/locale/eu',['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var eu = moment.defineLocale('eu', { + months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'), + monthsShort : 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'), + monthsParseExact : true, + weekdays : 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'), + weekdaysShort : 'ig._al._ar._az._og._ol._lr.'.split('_'), + weekdaysMin : 'ig_al_ar_az_og_ol_lr'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY-MM-DD', + LL : 'YYYY[ko] MMMM[ren] D[a]', + LLL : 'YYYY[ko] MMMM[ren] D[a] HH:mm', + LLLL : 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm', + l : 'YYYY-M-D', + ll : 'YYYY[ko] MMM D[a]', + lll : 'YYYY[ko] MMM D[a] HH:mm', + llll : 'ddd, YYYY[ko] MMM D[a] HH:mm' + }, + calendar : { + sameDay : '[gaur] LT[etan]', + nextDay : '[bihar] LT[etan]', + nextWeek : 'dddd LT[etan]', + lastDay : '[atzo] LT[etan]', + lastWeek : '[aurreko] dddd LT[etan]', + sameElse : 'L' + }, + relativeTime : { + future : '%s barru', + past : 'duela %s', + s : 'segundo batzuk', + m : 'minutu bat', + mm : '%d minutu', + h : 'ordu bat', + hh : '%d ordu', + d : 'egun bat', + dd : '%d egun', + M : 'hilabete bat', + MM : '%d hilabete', + y : 'urte bat', + yy : '%d urte' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); + +return eu; + +}))); + //! moment.js locale configuration //! locale : French [fr] //! author : John Fischer : https://github.com/jfroffice @@ -29744,8 +29811,6 @@ return zhTw; }))); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -29757,8 +29822,8 @@ return zhTw; /*global define */ (function (root, factory) { - define('i18n',["es6-promise", "jed", "lodash.noconflict", "moment", 'moment/locale/af', 'moment/locale/ar', 'moment/locale/bg', 'moment/locale/ca', 'moment/locale/de', 'moment/locale/es', 'moment/locale/fr', 'moment/locale/he', 'moment/locale/hu', 'moment/locale/id', 'moment/locale/it', 'moment/locale/ja', 'moment/locale/nb', 'moment/locale/nl', 'moment/locale/pl', 'moment/locale/pt-br', 'moment/locale/ru', 'moment/locale/tr', 'moment/locale/uk', 'moment/locale/zh-cn', 'moment/locale/zh-tw'], factory); -})(void 0, function (Promise, Jed, _, moment) { + define('i18n',["es6-promise", "jed", "lodash.noconflict", "moment", 'moment/locale/af', 'moment/locale/ar', 'moment/locale/bg', 'moment/locale/ca', 'moment/locale/de', 'moment/locale/es', 'moment/locale/eu', 'moment/locale/fr', 'moment/locale/he', 'moment/locale/hu', 'moment/locale/id', 'moment/locale/it', 'moment/locale/ja', 'moment/locale/nb', 'moment/locale/nl', 'moment/locale/pl', 'moment/locale/pt-br', 'moment/locale/ru', 'moment/locale/tr', 'moment/locale/uk', 'moment/locale/zh-cn', 'moment/locale/zh-tw'], factory); +})(this, function (Promise, Jed, _, moment) { 'use strict'; function detectLocale(library_check) { @@ -36232,8 +36297,6 @@ require(["strophe-polyfill"]); })); /* jshint ignore:end */ ; - - function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } // Converse.js (A browser based XMPP chat client) @@ -36248,7 +36311,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat /*global define, escape, window */ (function (root, factory) { define('utils',["sizzle", "es6-promise", "lodash.noconflict", "strophe"], factory); -})(void 0, function (sizzle, Promise, _, Strophe) { +})(this, function (sizzle, Promise, _, Strophe) { "use strict"; var b64_sha1 = Strophe.SHA1.b64_sha1; @@ -36463,6 +36526,14 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat })); }; + u.renderMovieURLs = function (obj) { + return ""; + }; + + u.renderAudioURLs = function (obj) { + return ""; + }; + u.slideInAllElements = function (elements) { var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 300; return Promise.all(_.map(elements, _.partial(u.slideIn, _, duration))); @@ -36977,9 +37048,19 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat el.dispatchEvent(evt); }; + u.geoUriToHttp = function (text, _converse) { + var regex = /geo:([\-0-9.]+),([\-0-9.]+)(?:,([\-0-9.]+))?(?:\?(.*))?/g; + return text.replace(regex, _converse.geouri_replacement); + }; + + u.httpToGeoUri = function (text, _converse) { + var replacement = 'geo:$1,$2'; + return text.replace(_converse.geouri_regex, replacement); + }; + return u; }); -//# sourceMappingURL=utils.js.map; +//# sourceMappingURL=core.js.map; (function (global, factory) { if (typeof define === "function" && define.amd) { define('pluggable',['exports', 'lodash'], factory); @@ -39647,8 +39728,6 @@ Backbone.sync = function(method, model, options) { return Backbone.BrowserStorage; })); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -39659,7 +39738,7 @@ return Backbone.BrowserStorage; /*global Backbone, define, window, JSON */ (function (root, factory) { define('converse-core',["sizzle", "es6-promise", "lodash.noconflict", "lodash.fp", "polyfill", "i18n", "utils", "moment", "strophe", "pluggable", "backbone.noconflict", "backbone.nativeview", "backbone.browserStorage"], factory); -})(void 0, function (sizzle, Promise, _, f, polyfill, i18n, u, moment, Strophe, pluggable, Backbone) { +})(this, function (sizzle, Promise, _, f, polyfill, i18n, u, moment, Strophe, pluggable, Backbone) { /* Cannot use this due to Safari bug. * See https://github.com/jcbrand/converse.js/issues/196 */ @@ -39708,7 +39787,7 @@ return Backbone.BrowserStorage; _.extend(_converse, Backbone.Events); // Core plugins are whitelisted automatically - _converse.core_plugins = ['converse-bookmarks', 'converse-chatboxes', 'converse-chatview', 'converse-controlbox', 'converse-core', 'converse-disco', 'converse-dragresize', 'converse-dropdown', 'converse-fullscreen', 'converse-headline', 'converse-mam', 'converse-minimize', 'converse-modal', 'converse-muc', 'converse-muc-embedded', 'converse-muc-views', 'converse-notification', 'converse-otr', 'converse-ping', 'converse-profile', 'converse-register', 'converse-roomslist', 'converse-rosterview', 'converse-singleton', 'converse-spoilers', 'converse-vcard']; // Make converse pluggable + _converse.core_plugins = ['converse-bookmarks', 'converse-chatboxes', 'converse-chatview', 'converse-controlbox', 'converse-core', 'converse-disco', 'converse-dragresize', 'converse-dropdown', 'converse-fullscreen', 'converse-headline', 'converse-httpFileUpload', 'converse-mam', 'converse-minimize', 'converse-modal', 'converse-muc', 'converse-muc-embedded', 'converse-muc-views', 'converse-notification', 'converse-otr', 'converse-ping', 'converse-profile', 'converse-register', 'converse-roomslist', 'converse-rosterview', 'converse-singleton', 'converse-spoilers', 'converse-vcard']; // Make converse pluggable pluggable.enable(_converse, '_converse', 'pluggable'); // Module-level constants @@ -39933,12 +40012,14 @@ return Backbone.BrowserStorage; expose_rid_and_sid: false, filter_by_resource: false, forward_messages: false, + geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g, + geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2', hide_offline_users: false, include_offline_state: false, jid: undefined, keepalive: true, locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json', - locales: ['af', 'ar', 'bg', 'ca', 'de', 'es', 'en', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'nb', 'nl', 'pl', 'pt_BR', 'ru', 'tr', 'uk', 'zh_CN', 'zh_TW'], + locales: ['af', 'ar', 'bg', 'ca', 'de', 'es', 'eu', 'en', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'nb', 'nl', 'pl', 'pt_BR', 'ru', 'tr', 'uk', 'zh_CN', 'zh_TW'], message_carbons: true, message_storage: 'session', nickname: undefined, @@ -40748,7 +40829,7 @@ return Backbone.BrowserStorage; } } }, - removeFromRoster: function removeFromRoster(callback) { + removeFromRoster: function removeFromRoster(callback, errback) { /* Instruct the XMPP server to remove this contact from our roster * Parameters: * (Function) callback @@ -40762,7 +40843,7 @@ return Backbone.BrowserStorage; subscription: "remove" }); - _converse.connection.sendIQ(iq, callback, callback); + _converse.connection.sendIQ(iq, callback, errback); return this; } @@ -46216,6 +46297,12 @@ function print() { __p += __j.call(arguments, '') } if (o.use_emoji) { ; __p += '\n
  • \n \n
  • \n'; } ; +__p += '\n'; + if (o.show_fileUpload_button) { ; +__p += '\n\n
  • \n \n
  • \n'; + } ; __p += '\n'; if (o.show_call_button) { ; __p += '\n
  • 2 && arguments[2] !== undefined ? arguments[2] : null; + /* This method gets called once the user has typed a message * and then pressed enter in a chat box. * @@ -47763,7 +48155,17 @@ return __p var attrs = this.getOutgoingMessageAttributes(text, spoiler_hint); var message = this.model.messages.create(attrs); - this.sendMessage(message); + /* check, if a file was send. If true it will send the file with XEP-0066. */ + + var messageStanza; + + if (file !== null) { + messageStanza = this.createFileMessageStanza(message); + } else { + messageStanza = this.createMessageStanza(message); + } + + this.sendMessage(messageStanza); }, getOutgoingMessageAttributes: function getOutgoingMessageAttributes(text, spoiler_hint) { /* Overridable method which returns the attributes to be @@ -47775,7 +48177,7 @@ return __p 'fullname': _.isEmpty(fullname) ? _converse.bare_jid : fullname, 'sender': 'me', 'time': moment().format(), - 'message': emojione.shortnameToUnicode(text), + 'message': u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse), 'is_spoiler': is_spoiler }; @@ -49926,8 +50328,6 @@ exports.default = vnode; }); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy8ucmVnaXN0cnkubnBtanMub3JnL2Jyb3dzZXItcGFjay82LjAuMi9ub2RlX21vZHVsZXMvYnJvd3Nlci1wYWNrL19wcmVsdWRlLmpzIiwiaHRtbGRvbWFwaS5qcyIsInRvdm5vZGUuanMiLCJ2bm9kZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5mdW5jdGlvbiBjcmVhdGVFbGVtZW50KHRhZ05hbWUpIHtcbiAgICByZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0YWdOYW1lKTtcbn1cbmZ1bmN0aW9uIGNyZWF0ZUVsZW1lbnROUyhuYW1lc3BhY2VVUkksIHF1YWxpZmllZE5hbWUpIHtcbiAgICByZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKG5hbWVzcGFjZVVSSSwgcXVhbGlmaWVkTmFtZSk7XG59XG5mdW5jdGlvbiBjcmVhdGVUZXh0Tm9kZSh0ZXh0KSB7XG4gICAgcmV0dXJuIGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRleHQpO1xufVxuZnVuY3Rpb24gY3JlYXRlQ29tbWVudCh0ZXh0KSB7XG4gICAgcmV0dXJuIGRvY3VtZW50LmNyZWF0ZUNvbW1lbnQodGV4dCk7XG59XG5mdW5jdGlvbiBpbnNlcnRCZWZvcmUocGFyZW50Tm9kZSwgbmV3Tm9kZSwgcmVmZXJlbmNlTm9kZSkge1xuICAgIHBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKG5ld05vZGUsIHJlZmVyZW5jZU5vZGUpO1xufVxuZnVuY3Rpb24gcmVtb3ZlQ2hpbGQobm9kZSwgY2hpbGQpIHtcbiAgICBub2RlLnJlbW92ZUNoaWxkKGNoaWxkKTtcbn1cbmZ1bmN0aW9uIGFwcGVuZENoaWxkKG5vZGUsIGNoaWxkKSB7XG4gICAgbm9kZS5hcHBlbmRDaGlsZChjaGlsZCk7XG59XG5mdW5jdGlvbiBwYXJlbnROb2RlKG5vZGUpIHtcbiAgICByZXR1cm4gbm9kZS5wYXJlbnROb2RlO1xufVxuZnVuY3Rpb24gbmV4dFNpYmxpbmcobm9kZSkge1xuICAgIHJldHVybiBub2RlLm5leHRTaWJsaW5nO1xufVxuZnVuY3Rpb24gdGFnTmFtZShlbG0pIHtcbiAgICByZXR1cm4gZWxtLnRhZ05hbWU7XG59XG5mdW5jdGlvbiBzZXRUZXh0Q29udGVudChub2RlLCB0ZXh0KSB7XG4gICAgbm9kZS50ZXh0Q29udGVudCA9IHRleHQ7XG59XG5mdW5jdGlvbiBnZXRUZXh0Q29udGVudChub2RlKSB7XG4gICAgcmV0dXJuIG5vZGUudGV4dENvbnRlbnQ7XG59XG5mdW5jdGlvbiBpc0VsZW1lbnQobm9kZSkge1xuICAgIHJldHVybiBub2RlLm5vZGVUeXBlID09PSAxO1xufVxuZnVuY3Rpb24gaXNUZXh0KG5vZGUpIHtcbiAgICByZXR1cm4gbm9kZS5ub2RlVHlwZSA9PT0gMztcbn1cbmZ1bmN0aW9uIGlzQ29tbWVudChub2RlKSB7XG4gICAgcmV0dXJuIG5vZGUubm9kZVR5cGUgPT09IDg7XG59XG5leHBvcnRzLmh0bWxEb21BcGkgPSB7XG4gICAgY3JlYXRlRWxlbWVudDogY3JlYXRlRWxlbWVudCxcbiAgICBjcmVhdGVFbGVtZW50TlM6IGNyZWF0ZUVsZW1lbnROUyxcbiAgICBjcmVhdGVUZXh0Tm9kZTogY3JlYXRlVGV4dE5vZGUsXG4gICAgY3JlYXRlQ29tbWVudDogY3JlYXRlQ29tbWVudCxcbiAgICBpbnNlcnRCZWZvcmU6IGluc2VydEJlZm9yZSxcbiAgICByZW1vdmVDaGlsZDogcmVtb3ZlQ2hpbGQsXG4gICAgYXBwZW5kQ2hpbGQ6IGFwcGVuZENoaWxkLFxuICAgIHBhcmVudE5vZGU6IHBhcmVudE5vZGUsXG4gICAgbmV4dFNpYmxpbmc6IG5leHRTaWJsaW5nLFxuICAgIHRhZ05hbWU6IHRhZ05hbWUsXG4gICAgc2V0VGV4dENvbnRlbnQ6IHNldFRleHRDb250ZW50LFxuICAgIGdldFRleHRDb250ZW50OiBnZXRUZXh0Q29udGVudCxcbiAgICBpc0VsZW1lbnQ6IGlzRWxlbWVudCxcbiAgICBpc1RleHQ6IGlzVGV4dCxcbiAgICBpc0NvbW1lbnQ6IGlzQ29tbWVudCxcbn07XG5leHBvcnRzLmRlZmF1bHQgPSBleHBvcnRzLmh0bWxEb21BcGk7XG4vLyMgc291cmNlTWFwcGluZ1VSTD1odG1sZG9tYXBpLmpzLm1hcCIsIlwidXNlIHN0cmljdFwiO1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xudmFyIHZub2RlXzEgPSByZXF1aXJlKFwiLi92bm9kZVwiKTtcbnZhciBodG1sZG9tYXBpXzEgPSByZXF1aXJlKFwiLi9odG1sZG9tYXBpXCIpO1xuZnVuY3Rpb24gdG9WTm9kZShub2RlLCBkb21BcGkpIHtcbiAgICB2YXIgYXBpID0gZG9tQXBpICE9PSB1bmRlZmluZWQgPyBkb21BcGkgOiBodG1sZG9tYXBpXzEuZGVmYXVsdDtcbiAgICB2YXIgdGV4dDtcbiAgICBpZiAoYXBpLmlzRWxlbWVudChub2RlKSkge1xuICAgICAgICB2YXIgaWQgPSBub2RlLmlkID8gJyMnICsgbm9kZS5pZCA6ICcnO1xuICAgICAgICB2YXIgY24gPSBub2RlLmdldEF0dHJpYnV0ZSgnY2xhc3MnKTtcbiAgICAgICAgdmFyIGMgPSBjbiA/ICcuJyArIGNuLnNwbGl0KCcgJykuam9pbignLicpIDogJyc7XG4gICAgICAgIHZhciBzZWwgPSBhcGkudGFnTmFtZShub2RlKS50b0xvd2VyQ2FzZSgpICsgaWQgKyBjO1xuICAgICAgICB2YXIgYXR0cnMgPSB7fTtcbiAgICAgICAgdmFyIGNoaWxkcmVuID0gW107XG4gICAgICAgIHZhciBuYW1lXzE7XG4gICAgICAgIHZhciBpID0gdm9pZCAwLCBuID0gdm9pZCAwO1xuICAgICAgICB2YXIgZWxtQXR0cnMgPSBub2RlLmF0dHJpYnV0ZXM7XG4gICAgICAgIHZhciBlbG1DaGlsZHJlbiA9IG5vZGUuY2hpbGROb2RlcztcbiAgICAgICAgZm9yIChpID0gMCwgbiA9IGVsbUF0dHJzLmxlbmd0aDsgaSA8IG47IGkrKykge1xuICAgICAgICAgICAgbmFtZV8xID0gZWxtQXR0cnNbaV0ubm9kZU5hbWU7XG4gICAgICAgICAgICBpZiAobmFtZV8xICE9PSAnaWQnICYmIG5hbWVfMSAhPT0gJ2NsYXNzJykge1xuICAgICAgICAgICAgICAgIGF0dHJzW25hbWVfMV0gPSBlbG1BdHRyc1tpXS5ub2RlVmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChpID0gMCwgbiA9IGVsbUNoaWxkcmVuLmxlbmd0aDsgaSA8IG47IGkrKykge1xuICAgICAgICAgICAgY2hpbGRyZW4ucHVzaCh0b1ZOb2RlKGVsbUNoaWxkcmVuW2ldKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHZub2RlXzEuZGVmYXVsdChzZWwsIHsgYXR0cnM6IGF0dHJzIH0sIGNoaWxkcmVuLCB1bmRlZmluZWQsIG5vZGUpO1xuICAgIH1cbiAgICBlbHNlIGlmIChhcGkuaXNUZXh0KG5vZGUpKSB7XG4gICAgICAgIHRleHQgPSBhcGkuZ2V0VGV4dENvbnRlbnQobm9kZSk7XG4gICAgICAgIHJldHVybiB2bm9kZV8xLmRlZmF1bHQodW5kZWZpbmVkLCB1bmRlZmluZWQsIHVuZGVmaW5lZCwgdGV4dCwgbm9kZSk7XG4gICAgfVxuICAgIGVsc2UgaWYgKGFwaS5pc0NvbW1lbnQobm9kZSkpIHtcbiAgICAgICAgdGV4dCA9IGFwaS5nZXRUZXh0Q29udGVudChub2RlKTtcbiAgICAgICAgcmV0dXJuIHZub2RlXzEuZGVmYXVsdCgnIScsIHt9LCBbXSwgdGV4dCwgbm9kZSk7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICByZXR1cm4gdm5vZGVfMS5kZWZhdWx0KCcnLCB7fSwgW10sIHVuZGVmaW5lZCwgdW5kZWZpbmVkKTtcbiAgICB9XG59XG5leHBvcnRzLnRvVk5vZGUgPSB0b1ZOb2RlO1xuZXhwb3J0cy5kZWZhdWx0ID0gdG9WTm9kZTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPXRvdm5vZGUuanMubWFwIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5mdW5jdGlvbiB2bm9kZShzZWwsIGRhdGEsIGNoaWxkcmVuLCB0ZXh0LCBlbG0pIHtcbiAgICB2YXIga2V5ID0gZGF0YSA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogZGF0YS5rZXk7XG4gICAgcmV0dXJuIHsgc2VsOiBzZWwsIGRhdGE6IGRhdGEsIGNoaWxkcmVuOiBjaGlsZHJlbixcbiAgICAgICAgdGV4dDogdGV4dCwgZWxtOiBlbG0sIGtleToga2V5IH07XG59XG5leHBvcnRzLnZub2RlID0gdm5vZGU7XG5leHBvcnRzLmRlZmF1bHQgPSB2bm9kZTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPXZub2RlLmpzLm1hcCJdfQ== ; - - function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } /*! @@ -49942,7 +50342,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat // CommonJS-like environments module.exports = factory(require('snabbdom'), require('snabbdom-attributes'), require('snabbdom-class'), require('snabbdom-dataset'), require('snabbdom-props'), require('snabbdom-style'), require('tovnode'), require('underscore'), require('backbone')); } -})(void 0, function (snabbdom, snabbdom_attributes, snabbdom_class, snabbdom_dataset, snabbdom_props, snabbdom_style, tovnode, _, Backbone) { +})(this, function (snabbdom, snabbdom_attributes, snabbdom_class, snabbdom_dataset, snabbdom_props, snabbdom_style, tovnode, _, Backbone) { "use strict"; var domParser = new DOMParser(); @@ -50011,13 +50411,11 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat return Backbone.VDOMView; }); //# sourceMappingURL=backbone.vdomview.js.map; - - (function (root, factory) { if (typeof define === 'function' && define.amd) { define('converse-modal',["converse-core", "bootstrap", "underscore", "backbone", "backbone.vdomview"], factory); } -})(void 0, function (converse, bootstrap, _, Backbone) { +})(this, function (converse, bootstrap, _, Backbone) { "use strict"; converse.plugins.add('converse-modal', { @@ -50054,8 +50452,6 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat }); }); //# sourceMappingURL=converse-modal.js.map; - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -50066,7 +50462,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat /*global define */ (function (root, factory) { define('converse-rosterview',["converse-core", "tpl!add_contact_modal", "tpl!group_header", "tpl!pending_contact", "tpl!requesting_contact", "tpl!roster", "tpl!roster_filter", "tpl!roster_item", "tpl!search_contact", "awesomplete", "converse-chatboxes", "converse-modal"], factory); -})(void 0, function (converse, tpl_add_contact_modal, tpl_group_header, tpl_pending_contact, tpl_requesting_contact, tpl_roster, tpl_roster_filter, tpl_roster_item, tpl_search_contact, Awesomplete) { +})(this, function (converse, tpl_add_contact_modal, tpl_group_header, tpl_pending_contact, tpl_requesting_contact, tpl_roster, tpl_roster_filter, tpl_roster_item, tpl_search_contact, Awesomplete) { "use strict"; var _converse$env = converse.env, @@ -50548,16 +50944,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat var result = confirm(__("Are you sure you want to remove this contact?")); if (result === true) { - var iq = $iq({ - type: 'set' - }).c('query', { - xmlns: Strophe.NS.ROSTER - }).c('item', { - jid: this.model.get('jid'), - subscription: "remove" - }); - - _converse.connection.sendIQ(iq, function (iq) { + this.model.removeFromRoster(function (iq) { _this.model.destroy(); _this.remove(); @@ -51323,8 +51710,6 @@ return __p }); })); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -51335,7 +51720,7 @@ return __p /*global define */ (function (root, factory) { define('converse-vcard',["converse-core", "strophe.vcard"], factory); -})(void 0, function (converse) { +})(this, function (converse) { "use strict"; var _converse$env = converse.env, @@ -51547,8 +51932,6 @@ return __p }); }); //# sourceMappingURL=converse-vcard.js.map; - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -51559,7 +51942,7 @@ return __p /*global define */ (function (root, factory) { define('converse-profile',["converse-core", "bootstrap", "tpl!chat_status_modal", "tpl!profile_modal", "tpl!profile_view", "tpl!status_option", "converse-vcard", "converse-modal"], factory); -})(void 0, function (converse, bootstrap, tpl_chat_status_modal, tpl_profile_modal, tpl_profile_view, tpl_status_option) { +})(this, function (converse, bootstrap, tpl_chat_status_modal, tpl_profile_modal, tpl_profile_view, tpl_status_option) { "use strict"; var _converse$env = converse.env, @@ -51701,8 +52084,6 @@ return __p }); }); //# sourceMappingURL=converse-profile.js.map; - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -51713,7 +52094,7 @@ return __p /*global define */ (function (root, factory) { define('converse-controlbox',["converse-core", "lodash.fp", "tpl!converse_brand_heading", "tpl!controlbox", "tpl!controlbox_toggle", "tpl!login_panel", "converse-chatview", "converse-rosterview", "converse-profile"], factory); -})(void 0, function (converse, fp, tpl_brand_heading, tpl_controlbox, tpl_controlbox_toggle, tpl_login_panel) { +})(this, function (converse, fp, tpl_brand_heading, tpl_controlbox, tpl_controlbox_toggle, tpl_login_panel) { "use strict"; var CHATBOX_TYPE = 'chatbox'; @@ -52485,8 +52866,6 @@ __e(o.value) + return __p };}); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -52499,7 +52878,7 @@ return __p /*global define, escape, Jed */ (function (root, factory) { define('form-utils',["sizzle", "lodash.noconflict", "utils", "tpl!field", "tpl!select_option", "tpl!form_select", "tpl!form_textarea", "tpl!form_checkbox", "tpl!form_username", "tpl!form_input", "tpl!form_captcha", "tpl!form_url"], factory); -})(void 0, function (sizzle, _, u, tpl_field, tpl_select_option, tpl_form_select, tpl_form_textarea, tpl_form_checkbox, tpl_form_username, tpl_form_input, tpl_form_captcha, tpl_form_url) { +})(this, function (sizzle, _, u, tpl_field, tpl_select_option, tpl_form_select, tpl_form_textarea, tpl_form_checkbox, tpl_form_username, tpl_form_input, tpl_form_captcha, tpl_form_url) { "use strict"; var XFORM_TYPE_MAP = { @@ -52628,7 +53007,7 @@ return __p return u; }); -//# sourceMappingURL=form-utils.js.map; +//# sourceMappingURL=form.js.map; /* Copyright 2010, François de Metz */ @@ -52893,8 +53272,6 @@ Strophe.addConnectionPlugin('disco', }); })); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -52907,7 +53284,7 @@ Strophe.addConnectionPlugin('disco', /*global Backbone, define, window */ (function (root, factory) { define('converse-disco',["converse-core", "sizzle", "strophe.disco"], factory); -})(void 0, function (converse, sizzle) { +})(this, function (converse, sizzle) { var _converse$env = converse.env, Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, @@ -53312,8 +53689,112 @@ Strophe.addConnectionPlugin('disco', }); //# sourceMappingURL=backbone.orderedlistview.js.map; +// Converse.js (A browser based XMPP chat client) +// http://conversejs.org +// +// This is the utilities module. +// +// Copyright (c) 2012-2017, Jan-Carel Brand +// Licensed under the Mozilla Public License (MPLv2) +// + +/*global define, escape, Jed */ +(function (root, factory) { + define('muc-utils',["converse-core", "utils"], factory); +})(this, function (converse, u) { + "use strict"; + + var _converse$env = converse.env, + Strophe = _converse$env.Strophe, + sizzle = _converse$env.sizzle, + _ = _converse$env._; + + u.computeAffiliationsDelta = function computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list) { + /* Given two lists of objects with 'jid', 'affiliation' and + * 'reason' properties, return a new list containing + * those objects that are new, changed or removed + * (depending on the 'remove_absentees' boolean). + * + * The affiliations for new and changed members stay the + * same, for removed members, the affiliation is set to 'none'. + * + * The 'reason' property is not taken into account when + * comparing whether affiliations have been changed. + * + * Parameters: + * (Boolean) exclude_existing: Indicates whether JIDs from + * the new list which are also in the old list + * (regardless of affiliation) should be excluded + * from the delta. One reason to do this + * would be when you want to add a JID only if it + * doesn't have *any* existing affiliation at all. + * (Boolean) remove_absentees: Indicates whether JIDs + * from the old list which are not in the new list + * should be considered removed and therefore be + * included in the delta with affiliation set + * to 'none'. + * (Array) new_list: Array containing the new affiliations + * (Array) old_list: Array containing the old affiliations + */ + var new_jids = _.map(new_list, 'jid'); + + var old_jids = _.map(old_list, 'jid'); // Get the new affiliations + var delta = _.map(_.difference(new_jids, old_jids), function (jid) { + return new_list[_.indexOf(new_jids, jid)]; + }); + + if (!exclude_existing) { + // Get the changed affiliations + delta = delta.concat(_.filter(new_list, function (item) { + var idx = _.indexOf(old_jids, item.jid); + + if (idx >= 0) { + return item.affiliation !== old_list[idx].affiliation; + } + + return false; + })); + } + + if (remove_absentees) { + // Get the removed affiliations + delta = delta.concat(_.map(_.difference(old_jids, new_jids), function (jid) { + return { + 'jid': jid, + 'affiliation': 'none' + }; + })); + } + + return delta; + }; + + u.parseMemberListIQ = function parseMemberListIQ(iq) { + /* Given an IQ stanza with a member list, create an array of member + * objects. + */ + return _.map(sizzle("query[xmlns=\"".concat(Strophe.NS.MUC_ADMIN, "\"] item"), iq), function (item) { + return { + 'jid': item.getAttribute('jid'), + 'affiliation': item.getAttribute('affiliation') + }; + }); + }; + + u.marshallAffiliationIQs = function marshallAffiliationIQs() { + /* Marshall a list of IQ stanzas into a map of JIDs and + * affiliations. + * + * Parameters: + * Any amount of XMLElement objects, representing the IQ + * stanzas. + */ + return _.flatMap(arguments[0], u.parseMemberListIQ); + }; +}); +//# sourceMappingURL=muc.js.map; // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -53327,8 +53808,8 @@ Strophe.addConnectionPlugin('disco', * specified in XEP-0045 Multi-user chat. */ (function (root, factory) { - define('converse-muc',["form-utils", "converse-core", "converse-chatview", "converse-disco", "backbone.overview", "backbone.orderedlistview", "backbone.vdomview"], factory); -})(void 0, function (u, converse) { + define('converse-muc',["form-utils", "converse-core", "converse-chatview", "converse-disco", "backbone.overview", "backbone.orderedlistview", "backbone.vdomview", "muc-utils"], factory); +})(this, function (u, converse) { "use strict"; var MUC_ROLE_WEIGHTS = { @@ -53576,6 +54057,328 @@ Strophe.addConnectionPlugin('disco', 'type': converse.CHATROOMS_TYPE }); }, + directInvite: function directInvite(recipient, reason) { + /* Send a direct invitation as per XEP-0249 + * + * Parameters: + * (String) recipient - JID of the person being invited + * (String) reason - Optional reason for the invitation + */ + if (this.get('membersonly')) { + // When inviting to a members-only room, we first add + // the person to the member list by giving them an + // affiliation of 'member' (if they're not affiliated + // already), otherwise they won't be able to join. + var map = {}; + map[recipient] = 'member'; + + var deltaFunc = _.partial(u.computeAffiliationsDelta, true, false); + + this.updateMemberLists([{ + 'jid': recipient, + 'affiliation': 'member', + 'reason': reason + }], ['member', 'owner', 'admin'], deltaFunc); + } + + var attrs = { + 'xmlns': 'jabber:x:conference', + 'jid': this.get('jid') + }; + + if (reason !== null) { + attrs.reason = reason; + } + + if (this.get('password')) { + attrs.password = this.get('password'); + } + + var invitation = $msg({ + from: _converse.connection.jid, + to: recipient, + id: _converse.connection.getUniqueId() + }).c('x', attrs); + + _converse.connection.send(invitation); + + _converse.emit('roomInviteSent', { + 'room': this, + 'recipient': recipient, + 'reason': reason + }); + }, + sendConfiguration: function sendConfiguration(config, callback, errback) { + /* Send an IQ stanza with the room configuration. + * + * Parameters: + * (Array) config: The room configuration + * (Function) callback: Callback upon succesful IQ response + * The first parameter passed in is IQ containing the + * room configuration. + * The second is the response IQ from the server. + * (Function) errback: Callback upon error IQ response + * The first parameter passed in is IQ containing the + * room configuration. + * The second is the response IQ from the server. + */ + var iq = $iq({ + to: this.get('jid'), + type: "set" + }).c("query", { + xmlns: Strophe.NS.MUC_OWNER + }).c("x", { + xmlns: Strophe.NS.XFORM, + type: "submit" + }); + + _.each(config || [], function (node) { + iq.cnode(node).up(); + }); + + callback = _.isUndefined(callback) ? _.noop : _.partial(callback, iq.nodeTree); + errback = _.isUndefined(errback) ? _.noop : _.partial(errback, iq.nodeTree); + return _converse.connection.sendIQ(iq, callback, errback); + }, + parseRoomFeatures: function parseRoomFeatures(iq) { + /* Parses an IQ stanza containing the room's features. + * + * See http://xmpp.org/extensions/xep-0045.html#disco-roominfo + * + * + * + * + * + * + * + * + * + * + */ + var features = { + 'features_fetched': true, + 'name': iq.querySelector('identity').getAttribute('name') + }; + + _.each(iq.querySelectorAll('feature'), function (field) { + var fieldname = field.getAttribute('var'); + + if (!fieldname.startsWith('muc_')) { + if (fieldname === Strophe.NS.MAM) { + features.mam_enabled = true; + } + + return; + } + + features[fieldname.replace('muc_', '')] = true; + }); + + var desc_field = iq.querySelector('field[var="muc#roominfo_description"] value'); + + if (!_.isNull(desc_field)) { + features.description = desc_field.textContent; + } + + this.save(features); + }, + requestMemberList: function requestMemberList(affiliation) { + var _this = this; + + /* Send an IQ stanza to the server, asking it for the + * member-list of this room. + * + * See: http://xmpp.org/extensions/xep-0045.html#modifymember + * + * Parameters: + * (String) affiliation: The specific member list to + * fetch. 'admin', 'owner' or 'member'. + * + * Returns: + * A promise which resolves once the list has been + * retrieved. + */ + return new Promise(function (resolve, reject) { + affiliation = affiliation || 'member'; + var iq = $iq({ + to: _this.get('jid'), + type: "get" + }).c("query", { + xmlns: Strophe.NS.MUC_ADMIN + }).c("item", { + 'affiliation': affiliation + }); + + _converse.connection.sendIQ(iq, resolve, reject); + }); + }, + setAffiliation: function setAffiliation(affiliation, members) { + /* Send IQ stanzas to the server to set an affiliation for + * the provided JIDs. + * + * See: http://xmpp.org/extensions/xep-0045.html#modifymember + * + * XXX: Prosody doesn't accept multiple JIDs' affiliations + * being set in one IQ stanza, so as a workaround we send + * a separate stanza for each JID. + * Related ticket: https://prosody.im/issues/issue/795 + * + * Parameters: + * (String) affiliation: The affiliation + * (Object) members: A map of jids, affiliations and + * optionally reasons. Only those entries with the + * same affiliation as being currently set will be + * considered. + * + * Returns: + * A promise which resolves and fails depending on the + * XMPP server response. + */ + members = _.filter(members, function (member) { + return (// We only want those members who have the right + // affiliation (or none, which implies the provided one). + _.isUndefined(member.affiliation) || member.affiliation === affiliation + ); + }); + + var promises = _.map(members, _.bind(this.sendAffiliationIQ, this, affiliation)); + + return Promise.all(promises); + }, + saveAffiliationAndRole: function saveAffiliationAndRole(pres) { + /* Parse the presence stanza for the current user's + * affiliation. + * + * Parameters: + * (XMLElement) pres: A stanza. + */ + var item = sizzle("x[xmlns=\"".concat(Strophe.NS.MUC_USER, "\"] item"), pres).pop(); + var is_self = pres.querySelector("status[code='110']"); + + if (is_self && !_.isNil(item)) { + var affiliation = item.getAttribute('affiliation'); + var role = item.getAttribute('role'); + + if (affiliation) { + this.save({ + 'affiliation': affiliation + }); + } + + if (role) { + this.save({ + 'role': role + }); + } + } + }, + sendAffiliationIQ: function sendAffiliationIQ(affiliation, member) { + var _this2 = this; + + /* Send an IQ stanza specifying an affiliation change. + * + * Paremeters: + * (String) affiliation: affiliation (could also be stored + * on the member object). + * (Object) member: Map containing the member's jid and + * optionally a reason and affiliation. + */ + return new Promise(function (resolve, reject) { + var iq = $iq({ + to: _this2.get('jid'), + type: "set" + }).c("query", { + xmlns: Strophe.NS.MUC_ADMIN + }).c("item", { + 'affiliation': member.affiliation || affiliation, + 'jid': member.jid + }); + + if (!_.isUndefined(member.reason)) { + iq.c("reason", member.reason); + } + + _converse.connection.sendIQ(iq, resolve, reject); + }); + }, + setAffiliations: function setAffiliations(members) { + /* Send IQ stanzas to the server to modify the + * affiliations in this room. + * + * See: http://xmpp.org/extensions/xep-0045.html#modifymember + * + * Parameters: + * (Object) members: A map of jids, affiliations and optionally reasons + * (Function) onSuccess: callback for a succesful response + * (Function) onError: callback for an error response + */ + var affiliations = _.uniq(_.map(members, 'affiliation')); + + _.each(affiliations, _.partial(this.setAffiliation.bind(this), _, members)); + }, + getJidsWithAffiliations: function getJidsWithAffiliations(affiliations) { + var _this3 = this; + + /* Returns a map of JIDs that have the affiliations + * as provided. + */ + if (_.isString(affiliations)) { + affiliations = [affiliations]; + } + + return new Promise(function (resolve, reject) { + var promises = _.map(affiliations, _.partial(_this3.requestMemberList.bind(_this3))); + + Promise.all(promises).then(_.flow(u.marshallAffiliationIQs, resolve), _.flow(u.marshallAffiliationIQs, resolve)); + }); + }, + updateMemberLists: function updateMemberLists(members, affiliations, deltaFunc) { + var _this4 = this; + + /* Fetch the lists of users with the given affiliations. + * Then compute the delta between those users and + * the passed in members, and if it exists, send the delta + * to the XMPP server to update the member list. + * + * Parameters: + * (Object) members: Map of member jids and affiliations. + * (String|Array) affiliation: An array of affiliations or + * a string if only one affiliation. + * (Function) deltaFunc: The function to compute the delta + * between old and new member lists. + * + * Returns: + * A promise which is resolved once the list has been + * updated or once it's been established there's no need + * to update the list. + */ + this.getJidsWithAffiliations(affiliations).then(function (old_members) { + _this4.setAffiliations(deltaFunc(members, old_members)); + }); + }, + checkForReservedNick: function checkForReservedNick(callback, errback) { + /* Use service-discovery to ask the XMPP server whether + * this user has a reserved nickname for this room. + * If so, we'll use that, otherwise we render the nickname form. + * + * Parameters: + * (Function) callback: Callback upon succesful IQ response + * (Function) errback: Callback upon error IQ response + */ + _converse.connection.sendIQ($iq({ + 'to': this.get('jid'), + 'from': _converse.connection.jid, + 'type': "get" + }).c("query", { + 'xmlns': Strophe.NS.DISCO_INFO, + 'node': 'x-roomuser-item' + }), callback, errback); + + return this; + }, isUserMentioned: function isUserMentioned(message) { /* Returns a boolean to indicate whether the current user * was mentioned in a message. @@ -53599,17 +54402,17 @@ Strophe.addConnectionPlugin('disco', } if (u.isNewMessage(stanza) && this.newMessageWillBeHidden()) { - this.save({ + var settings = { 'num_unread_general': this.get('num_unread_general') + 1 - }); + }; if (this.isUserMentioned(body.textContent)) { - this.save({ - 'num_unread': this.get('num_unread') + 1 - }); + settings.num_unread = this.get('num_unread') + 1; _converse.incrementMsgCounter(); } + + this.save(settings); } }, clearUnreadMsgCounter: function clearUnreadMsgCounter() { @@ -53982,8 +54785,6 @@ __p += '">\n'; return __p };}); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -53998,7 +54799,7 @@ return __p */ (function (root, factory) { define('converse-bookmarks',["converse-core", "converse-muc", "tpl!chatroom_bookmark_form", "tpl!chatroom_bookmark_toggle", "tpl!bookmark", "tpl!bookmarks_list"], factory); -})(void 0, function (converse, muc, tpl_chatroom_bookmark_form, tpl_chatroom_bookmark_toggle, tpl_bookmark, tpl_bookmarks_list) { +})(this, function (converse, muc, tpl_chatroom_bookmark_form, tpl_chatroom_bookmark_toggle, tpl_bookmark, tpl_bookmarks_list) { var _converse$env = converse.env, Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, @@ -54369,7 +55170,7 @@ return __p 'jid': bookmark.getAttribute('jid'), 'name': bookmark.getAttribute('name'), 'autojoin': bookmark.getAttribute('autojoin') === 'true', - 'nick': bookmark.querySelector('nick').textContent + 'nick': _.get(bookmark.querySelector('nick'), 'textContent') }); }); }, @@ -54666,8 +55467,6 @@ __e(o.info_title) + return __p };}); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -54682,7 +55481,7 @@ return __p */ (function (root, factory) { define('converse-roomslist',["utils", "converse-core", "converse-muc", "tpl!rooms_list", "tpl!rooms_list_item"], factory); -})(void 0, function (utils, converse, muc, tpl_rooms_list, tpl_rooms_list_item) { +})(this, function (utils, converse, muc, tpl_rooms_list, tpl_rooms_list_item) { var _converse$env = converse.env, Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, @@ -55034,8 +55833,6 @@ Strophe.RSM.prototype = { }; })); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -55047,7 +55844,7 @@ Strophe.RSM.prototype = { // XEP-0059 Result Set Management (function (root, factory) { define('converse-mam',["sizzle", "converse-core", "utils", "converse-disco", "strophe.rsm"], factory); -})(void 0, function (sizzle, converse, utils) { +})(this, function (sizzle, converse, utils) { "use strict"; var CHATROOMS_TYPE = 'chatroom'; @@ -55594,7 +56391,7 @@ return __p define('tpl!chatarea', ['lodash'], function(_) {return function(o) { var __t, __p = '', __e = _.escape, __j = Array.prototype.join; function print() { __p += __j.call(arguments, '') } -__p += '
    \n
    \n
    \n \n \n
  • \n'; } ; +__p += '\n'; + if (o.show_fileUpload_button) { ; +__p += '\n\n
  • \n \n
  • \n'; + } ; __p += '\n'; if (o.show_call_button) { ; __p += '\n
  • = 0) { - return item.affiliation !== old_list[idx].affiliation; - } - - return false; - })); - } - - if (remove_absentees) { - // Get the removed affiliations - delta = delta.concat(_.map(_.difference(old_jids, new_jids), function (jid) { - return { - 'jid': jid, - 'affiliation': 'none' - }; - })); - } - - return delta; - }, - sendAffiliationIQ: function sendAffiliationIQ(chatroom_jid, affiliation, member) { - /* Send an IQ stanza specifying an affiliation change. - * - * Paremeters: - * (String) chatroom_jid: JID of the relevant room - * (String) affiliation: affiliation (could also be stored - * on the member object). - * (Object) member: Map containing the member's jid and - * optionally a reason and affiliation. - */ - return new Promise(function (resolve, reject) { - var iq = $iq({ - to: chatroom_jid, - type: "set" - }).c("query", { - xmlns: Strophe.NS.MUC_ADMIN - }).c("item", { - 'affiliation': member.affiliation || affiliation, - 'jid': member.jid - }); - - if (!_.isUndefined(member.reason)) { - iq.c("reason", member.reason); - } - - _converse.connection.sendIQ(iq, resolve, reject); - }); - }, - setAffiliation: function setAffiliation(affiliation, members) { - /* Send IQ stanzas to the server to set an affiliation for - * the provided JIDs. - * - * See: http://xmpp.org/extensions/xep-0045.html#modifymember - * - * XXX: Prosody doesn't accept multiple JIDs' affiliations - * being set in one IQ stanza, so as a workaround we send - * a separate stanza for each JID. - * Related ticket: https://prosody.im/issues/issue/795 - * - * Parameters: - * (String) affiliation: The affiliation - * (Object) members: A map of jids, affiliations and - * optionally reasons. Only those entries with the - * same affiliation as being currently set will be - * considered. - * - * Returns: - * A promise which resolves and fails depending on the - * XMPP server response. - */ - members = _.filter(members, function (member) { - return (// We only want those members who have the right - // affiliation (or none, which implies the provided - // one). - _.isUndefined(member.affiliation) || member.affiliation === affiliation - ); - }); - - var promises = _.map(members, _.partial(this.sendAffiliationIQ, this.model.get('jid'), affiliation)); - - return Promise.all(promises); - }, - setAffiliations: function setAffiliations(members) { - /* Send IQ stanzas to the server to modify the - * affiliations in this room. - * - * See: http://xmpp.org/extensions/xep-0045.html#modifymember - * - * Parameters: - * (Object) members: A map of jids, affiliations and optionally reasons - * (Function) onSuccess: callback for a succesful response - * (Function) onError: callback for an error response - */ - var affiliations = _.uniq(_.map(members, 'affiliation')); - - _.each(affiliations, _.partial(this.setAffiliation.bind(this), _, members)); - }, - marshallAffiliationIQs: function marshallAffiliationIQs() { - /* Marshall a list of IQ stanzas into a map of JIDs and - * affiliations. - * - * Parameters: - * Any amount of XMLElement objects, representing the IQ - * stanzas. - */ - return _.flatMap(arguments[0], this.parseMemberListIQ); - }, - getJidsWithAffiliations: function getJidsWithAffiliations(affiliations) { - var _this4 = this; - - /* Returns a map of JIDs that have the affiliations - * as provided. - */ - if (_.isString(affiliations)) { - affiliations = [affiliations]; - } - - return new Promise(function (resolve, reject) { - var promises = _.map(affiliations, _.partial(_this4.requestMemberList, _this4.model.get('jid'))); - - Promise.all(promises).then(_.flow(_this4.marshallAffiliationIQs.bind(_this4), resolve), _.flow(_this4.marshallAffiliationIQs.bind(_this4), resolve)); - }); - }, - updateMemberLists: function updateMemberLists(members, affiliations, deltaFunc) { - var _this5 = this; - - /* Fetch the lists of users with the given affiliations. - * Then compute the delta between those users and - * the passed in members, and if it exists, send the delta - * to the XMPP server to update the member list. - * - * Parameters: - * (Object) members: Map of member jids and affiliations. - * (String|Array) affiliation: An array of affiliations or - * a string if only one affiliation. - * (Function) deltaFunc: The function to compute the delta - * between old and new member lists. - * - * Returns: - * A promise which is resolved once the list has been - * updated or once it's been established there's no need - * to update the list. - */ - this.getJidsWithAffiliations(affiliations).then(function (old_members) { - _this5.setAffiliations(deltaFunc(members, old_members)); - }); - }, - directInvite: function directInvite(recipient, reason) { - /* Send a direct invitation as per XEP-0249 - * - * Parameters: - * (String) recipient - JID of the person being invited - * (String) reason - Optional reason for the invitation - */ - if (this.model.get('membersonly')) { - // When inviting to a members-only room, we first add - // the person to the member list by giving them an - // affiliation of 'member' (if they're not affiliated - // already), otherwise they won't be able to join. - var map = {}; - map[recipient] = 'member'; - - var deltaFunc = _.partial(this.computeAffiliationsDelta, true, false); - - this.updateMemberLists([{ - 'jid': recipient, - 'affiliation': 'member', - 'reason': reason - }], ['member', 'owner', 'admin'], deltaFunc); - } - - var attrs = { - 'xmlns': 'jabber:x:conference', - 'jid': this.model.get('jid') - }; - - if (reason !== null) { - attrs.reason = reason; - } - - if (this.model.get('password')) { - attrs.password = this.model.get('password'); - } - - var invitation = $msg({ - from: _converse.connection.jid, - to: recipient, - id: _converse.connection.getUniqueId() - }).c('x', attrs); - - _converse.connection.send(invitation); - - _converse.emit('roomInviteSent', { - 'room': this, - 'recipient': recipient, - 'reason': reason - }); - }, handleChatStateMessage: function handleChatStateMessage(message) { /* Override the method on the ChatBoxView base class to * ignore notifications in groupchats. @@ -56973,7 +57497,7 @@ return __p * Parameters: * (String) text: The message text to be sent. */ - text = emojione.shortnameToUnicode(text); + text = u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse); var msgid = _converse.connection.getUniqueId(); @@ -56996,6 +57520,20 @@ return __p msgid: msgid }); }, + sendChatRoomFile: function sendChatRoomFile(text) { + var msgid = _converse.connection.getUniqueId(); + + var stanza = $msg({ + 'from': _converse.connection.jid, + 'to': this.model.get('jid'), + 'type': 'groupchat', + 'id': msgid + }).c("body").t(text).up().c("x", { + 'xmlns': Strophe.NS.OUTOFBAND + }).c('url').t(text).up(); + + _converse.connection.send(stanza); + }, modifyRole: function modifyRole(room, nick, role, reason, onSuccess, onError) { var item = $build("item", { nick: nick, @@ -57044,13 +57582,19 @@ return __p onCommandError: function onCommandError() { this.showStatusNotification(__("Error: could not execute the command"), true); }, - onMessageSubmitted: function onMessageSubmitted(text) { + onMessageSubmitted: function onMessageSubmitted(text, notNeeded) { + var file = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; + /* Gets called when the user presses enter to send off a * message in a chat room. * * Parameters: * (String) text - The message text. */ + if (file !== null) { + return this.sendChatRoomFile(text); + } + if (_converse.muc_disable_moderator_commands) { return this.sendChatRoomMessage(text); } @@ -57065,7 +57609,7 @@ return __p break; } - this.setAffiliation('admin', [{ + this.model.setAffiliation('admin', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57076,7 +57620,7 @@ return __p break; } - this.setAffiliation('outcast', [{ + this.model.setAffiliation('outcast', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57119,7 +57663,7 @@ return __p break; } - this.setAffiliation('member', [{ + this.model.setAffiliation('member', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57139,7 +57683,7 @@ return __p break; } - this.setAffiliation('owner', [{ + this.model.setAffiliation('owner', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57158,7 +57702,7 @@ return __p break; } - this.setAffiliation('none', [{ + this.model.setAffiliation('none', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57274,7 +57818,8 @@ return __p nick = nick ? nick : this.model.get('nick'); if (!nick) { - return this.checkForReservedNick(); + this.checkForReservedNick(); + return this; } if (this.model.get('connection_status') === converse.ROOMSTATUS.ENTERED) { @@ -57344,7 +57889,7 @@ return __p _converse.ChatBoxView.prototype.close.apply(this, arguments); }, renderConfigurationForm: function renderConfigurationForm(stanza) { - var _this6 = this; + var _this4 = this; /* Renders a form given an IQ stanza containing the current * room configuration. @@ -57389,48 +57934,16 @@ return __p last_fieldset_el.querySelector('input[type=button]').addEventListener('click', function (ev) { ev.preventDefault(); - _this6.closeForm(); + _this4.closeForm(); }); form_el.addEventListener('submit', function (ev) { ev.preventDefault(); - _this6.saveConfiguration(ev.target).then(_this6.getRoomFeatures.bind(_this6)); + _this4.saveConfiguration(ev.target).then(_this4.getRoomFeatures.bind(_this4)); }, false); }, - sendConfiguration: function sendConfiguration(config, onSuccess, onError) { - /* Send an IQ stanza with the room configuration. - * - * Parameters: - * (Array) config: The room configuration - * (Function) onSuccess: Callback upon succesful IQ response - * The first parameter passed in is IQ containing the - * room configuration. - * The second is the response IQ from the server. - * (Function) onError: Callback upon error IQ response - * The first parameter passed in is IQ containing the - * room configuration. - * The second is the response IQ from the server. - */ - var iq = $iq({ - to: this.model.get('jid'), - type: "set" - }).c("query", { - xmlns: Strophe.NS.MUC_OWNER - }).c("x", { - xmlns: Strophe.NS.XFORM, - type: "submit" - }); - - _.each(config || [], function (node) { - iq.cnode(node).up(); - }); - - onSuccess = _.isUndefined(onSuccess) ? _.noop : _.partial(onSuccess, iq.nodeTree); - onError = _.isUndefined(onError) ? _.noop : _.partial(onError, iq.nodeTree); - return _converse.connection.sendIQ(iq, onSuccess, onError); - }, saveConfiguration: function saveConfiguration(form) { - var _this7 = this; + var _this5 = this; /* Submit the room configuration form by sending an IQ * stanza to the server. @@ -57445,13 +57958,13 @@ return __p var inputs = form ? sizzle(':input:not([type=button]):not([type=submit])', form) : [], configArray = _.map(inputs, u.webForm2xForm); - _this7.sendConfiguration(configArray, resolve, reject); + _this5.model.sendConfiguration(configArray, resolve, reject); - _this7.closeForm(); + _this5.closeForm(); }); }, autoConfigureChatRoom: function autoConfigureChatRoom() { - var _this8 = this; + var _this6 = this; /* Automatically configure room based on the * 'roomconfig' data on this view's model. @@ -57465,7 +57978,7 @@ return __p */ var that = this; return new Promise(function (resolve, reject) { - _this8.fetchRoomConfiguration().then(function (stanza) { + _this6.fetchRoomConfiguration().then(function (stanza) { var configArray = [], fields = stanza.querySelectorAll('field'), config = that.model.get('roomconfig'); @@ -57497,7 +58010,7 @@ return __p configArray.push(field); if (! --count) { - that.sendConfiguration(configArray, resolve, reject); + that.model.sendConfiguration(configArray, resolve, reject); } }); }); @@ -57511,7 +58024,7 @@ return __p this.renderAfterTransition(); }, fetchRoomConfiguration: function fetchRoomConfiguration(handler) { - var _this9 = this, + var _this7 = this, _arguments = arguments; /* Send an IQ stanza to fetch the room configuration data. @@ -57523,13 +58036,13 @@ return __p */ return new Promise(function (resolve, reject) { _converse.connection.sendIQ($iq({ - 'to': _this9.model.get('jid'), + 'to': _this7.model.get('jid'), 'type': "get" }).c("query", { xmlns: Strophe.NS.MUC_OWNER }), function (iq) { if (handler) { - handler.apply(_this9, _arguments); + handler.apply(_this7, _arguments); } resolve(iq); @@ -57537,57 +58050,14 @@ return __p ); }); }, - parseRoomFeatures: function parseRoomFeatures(iq) { - /* See http://xmpp.org/extensions/xep-0045.html#disco-roominfo - * - * - * - * - * - * - * - * - * - * - */ - var features = { - 'features_fetched': true, - 'name': iq.querySelector('identity').getAttribute('name') - }; - - _.each(iq.querySelectorAll('feature'), function (field) { - var fieldname = field.getAttribute('var'); - - if (!fieldname.startsWith('muc_')) { - if (fieldname === Strophe.NS.MAM) { - features.mam_enabled = true; - } - - return; - } - - features[fieldname.replace('muc_', '')] = true; - }); - - var desc_field = iq.querySelector('field[var="muc#roominfo_description"] value'); - - if (!_.isNull(desc_field)) { - features.description = desc_field.textContent; - } - - this.model.save(features); - }, getRoomFeatures: function getRoomFeatures() { - var _this10 = this; + var _this8 = this; /* Fetch the room disco info, parse it and then * save it on the Backbone.Model of this chat rooms. */ return new Promise(function (resolve, reject) { - _converse.connection.disco.info(_this10.model.get('jid'), null, _.flow(_this10.parseRoomFeatures.bind(_this10), resolve), function () { + _converse.connection.disco.info(_this8.model.get('jid'), null, _.flow(_this8.model.parseRoomFeatures.bind(_this8.model), resolve), function () { reject(new Error("Could not parse the room features")); }, 5000); }); @@ -57631,20 +58101,10 @@ return __p checkForReservedNick: function checkForReservedNick() { /* User service-discovery to ask the XMPP server whether * this user has a reserved nickname for this room. - * If so, we'll use that, otherwise we render the nickname - * form. + * If so, we'll use that, otherwise we render the nickname form. */ this.showSpinner(); - - _converse.connection.sendIQ($iq({ - 'to': this.model.get('jid'), - 'from': _converse.connection.jid, - 'type': "get" - }).c("query", { - 'xmlns': Strophe.NS.DISCO_INFO, - 'node': 'x-roomuser-item' - }), this.onNickNameFound.bind(this), this.onNickNameNotFound.bind(this)); - + this.model.checkForReservedNick(this.onNickNameFound.bind(this), this.onNickNameNotFound.bind(this)); return this; }, onNickNameFound: function onNickNameFound(iq) { @@ -57805,33 +58265,6 @@ return __p return; }, - saveAffiliationAndRole: function saveAffiliationAndRole(pres) { - /* Parse the presence stanza for the current user's - * affiliation. - * - * Parameters: - * (XMLElement) pres: A stanza. - */ - var item = sizzle("x[xmlns=\"".concat(Strophe.NS.MUC_USER, "\"] item"), pres).pop(); - var is_self = pres.querySelector("status[code='110']"); - - if (is_self && !_.isNil(item)) { - var affiliation = item.getAttribute('affiliation'); - var role = item.getAttribute('role'); - - if (affiliation) { - this.model.save({ - 'affiliation': affiliation - }); - } - - if (role) { - this.model.save({ - 'role': role - }); - } - } - }, parseXUserElement: function parseXUserElement(x, stanza, is_self) { /* Parse the passed-in * element and construct a map containing relevant @@ -57885,7 +58318,7 @@ return __p return notification; }, displayNotificationsforUser: function displayNotificationsforUser(notification) { - var _this11 = this; + var _this9 = this; /* Given the notification object generated by * parseXUserElement, display any relevant messages and @@ -57907,7 +58340,7 @@ return __p } _.each(notification.messages, function (message) { - _this11.content.insertAdjacentHTML('beforeend', tpl_info({ + _this9.content.insertAdjacentHTML('beforeend', tpl_info({ 'data': '', 'isodate': moment().format(), 'extra_classes': 'chat-event', @@ -58133,7 +58566,7 @@ return __p * Parameters: * (XMLElement) pres: The stanza */ - this.saveAffiliationAndRole(pres); + this.model.saveAffiliationAndRole(pres); var locked_room = pres.querySelector("status[code='201']"); if (locked_room) { @@ -58606,7 +59039,7 @@ return __p var reason = prompt(__('You are about to invite %1$s to the chat room "%2$s". ' + 'You may optionally include a message, explaining the reason for the invitation.', suggestion.text.label, this.model.get('id'))); if (reason !== null) { - this.chatroomview.directInvite(suggestion.text.value, reason); + this.chatroomview.model.directInvite(suggestion.text.value, reason); } var form = suggestion.target.form, @@ -58732,8 +59165,6 @@ return __p }); }); //# sourceMappingURL=converse-muc-views.js.map; - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -58742,7 +59173,7 @@ return __p // (function (root, factory) { define('converse-muc-embedded',["converse-core", "converse-muc"], factory); -})(void 0, function (converse) { +})(this, function (converse) { "use strict"; var _converse$env = converse.env, @@ -66093,8 +66524,6 @@ CryptoJS.mode.CTR = (function () { } })); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -66109,7 +66538,7 @@ CryptoJS.mode.CTR = (function () { */ (function (root, factory) { define('converse-otr',["converse-chatview", "bootstrap", "tpl!toolbar_otr", 'otr'], factory); -})(void 0, function (converse, bootstrap, tpl_toolbar_otr, otr) { +})(this, function (converse, bootstrap, tpl_toolbar_otr, otr) { "use strict"; var _converse$env = converse.env, @@ -66705,8 +67134,6 @@ __p += '\n'; return __p };}); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -66721,7 +67148,7 @@ return __p */ (function (root, factory) { define('converse-register',["form-utils", "converse-core", "tpl!form_username", "tpl!register_link", "tpl!register_panel", "tpl!registration_form", "tpl!registration_request", "tpl!form_input", "tpl!spinner", "converse-controlbox"], factory); -})(void 0, function (utils, converse, tpl_form_username, tpl_register_link, tpl_register_panel, tpl_registration_form, tpl_registration_request, tpl_form_input, tpl_spinner) { +})(this, function (utils, converse, tpl_form_username, tpl_register_link, tpl_register_panel, tpl_registration_form, tpl_registration_request, tpl_form_input, tpl_spinner) { "use strict"; // Strophe methods for building stanzas var _converse$env = converse.env, @@ -67509,8 +67936,6 @@ return __p }); })); - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -67525,7 +67950,7 @@ return __p */ (function (root, factory) { define('converse-ping',["converse-core", "strophe.ping"], factory); -})(void 0, function (converse) { +})(this, function (converse) { "use strict"; // Strophe methods for building stanzas var _converse$env = converse.env, @@ -67633,8 +68058,6 @@ return __p }); }); //# sourceMappingURL=converse-ping.js.map; - - // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -67645,7 +68068,7 @@ return __p /*global define */ (function (root, factory) { define('converse-notification',["converse-core"], factory); -})(void 0, function (converse) { +})(this, function (converse) { "use strict"; var _converse$env = converse.env, @@ -67992,8 +68415,6 @@ __p += '\n Date: Wed, 4 Apr 2018 16:03:44 +0200 Subject: [PATCH 03/12] changed line-endings in converse-httpFileUpload.js --- src/converse-httpFileUpload.js | 538 ++++++++++++++++----------------- 1 file changed, 269 insertions(+), 269 deletions(-) diff --git a/src/converse-httpFileUpload.js b/src/converse-httpFileUpload.js index dfc4b3a01..0ea3ed982 100644 --- a/src/converse-httpFileUpload.js +++ b/src/converse-httpFileUpload.js @@ -1,269 +1,269 @@ -/* - The MIT License (MIT) - - Copyright (c) 2014 Klaus Herberth - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/** - * Implements Http File Upload (XEP-0363) - * - * @see {@link http://xmpp.org/extensions/xep-0363.html} - */ -(function (root, factory) { - define([ - "converse-core", - ], factory); -}(this, function ( - converse - ) { - "use strict"; - const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env; - - Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload'); - - var requestSlotUrl; - var ready; - var httpUploadOption = { - enable: true - } - - converse.plugins.add('converse-httpFileUpload', { - - dependencies: ["converse-chatboxes", "converse-disco"], - - initialize() { - const { _converse } = this, - { __ } = _converse; - var connection = _converse.connection; - var domain; - var file; - var chatBox; - - _converse.FileUpload = Backbone.NativeView.extend({ - /** - * Set up http file upload. - * - * @param {*} connection the current strophe-connection - */ - initFiletransfer () { - connection = _converse.connection; - domain = _converse.connection.domain; - - if (httpUploadOption && requestSlotUrl != undefined) { - ready = true; - return; - } - this.discoverUploadService(); - }, - - /** - * Discover upload service for http upload. - * - */ - discoverUploadService () { - var self = this; - console.log('discover http upload service'); - connection.disco.items(domain, null, function(items) { - var childs = items.getElementsByTagName('item'); - for(var i = 0; i < childs.length; i++){ - var jid = childs[i].attributes.jid.value; - if (ready) { - // abort, because we already found a service - return false; - } - self.queryItemForUploadService(jid); - } - }); - }, - - /** - * Query item for upload service. - * - * @param {String} jid of the logged-in user - * @param {Function} cb Callback on success - */ - queryItemForUploadService (jid) { - var self = this; - console.log('query ' + jid + ' for upload service'); - - connection.disco.info(jid, null, function(info) { - var httpUploadFeature; - var temp = info.getElementsByTagName('feature'); - for(var i = 0; i < temp.length; i++){ - var feature = temp[i].attributes.var; - if(feature != undefined && feature.value === Strophe.NS.HTTPUPLOAD){ - requestSlotUrl = jid; - ready = true; - self.sendFile(); - } - } - }); - }, - - /** - * Saves the file the user has picked. - * - * @param {*} file the name of the file the user has picked. - * @param {*} chatBox the chatbox from which the user initiated the file-upload - */ - setFile (file1, chatBox1){ - file = file1; - chatBox = chatBox1; - this.sendFile(); - }, - - /** - * Upload file. - * Waits till the Upload-Service is discovered and till the user has picked a file. - * - */ - sendFile () { - var self = this; - if(file === undefined){ - console.log("waiting to choose a file"); - return; - } - else if(requestSlotUrl === undefined){ - console.log("waiting for service discovery"); - return; - } - - console.log('Send file via http upload'); - chatBox.showHelpMessages([__('The file upload starts now')],'info'); - this.requestSlot(file, function(data) { - if (!data) { - // general error - console.log('Unknown error while requesting upload slot.'); - alert(__('File upload failed. Please check the log.')); - } else if (data.error) { - // specific error - console.log('The XMPP-Server return an error of the type: ' + data.error.type); - alert(__('File upload failed. Please check the log.')); - } else if (data.get && data.put) { - console.log('slot received, start upload to ' + data.put); - self.uploadFile(data.put, file, function() { - console.log(data.put); - - chatBox.onMessageSubmitted(data.put, null, file); - file = undefined; - }); - } - }); - }, - - /** - * Request upload slot from xmpp-server - * - * @param {File} file the file the user picked - * @param {Function} cb Callback after finished request - */ - requestSlot (file, cb) { - var self = this; - console.log("try sending file to: " + requestSlotUrl); - var iq = converse.env.$iq({ - to: requestSlotUrl, - type: 'get' - }).c('request', { - xmlns: Strophe.NS.HTTPUPLOAD - }).c('filename').t(file.name) - .up() - .c('size').t(file.size); - - connection.sendIQ(iq, function(stanza) { - self.successfulRequestSlotCB(stanza, cb); - }, function(stanza) { - self.failedRequestSlotCB(stanza, cb); - }); - }, - - /** - * Upload the given file to the given url. - * - * @param {String} url upload url - * @param {File} file the file the user picked - * @param {Function} success_cb callback on successful transition - */ - uploadFile (url, file, success_cb) { - console.log("uploadFile start"); - var xmlhttp = new XMLHttpRequest(); - var type = 'PUT'; - var contentType = 'application/octet-stream'; - var data = file; - var processData = false; - xmlhttp.onreadystatechange = function() { - if (xmlhttp.readyState == XMLHttpRequest.DONE) { - console.log("Status: " + xmlhttp.status); - if (xmlhttp.status == 200 || xmlhttp.status == 201) { - console.log('file successful uploaded'); - if (success_cb) { - success_cb(); - } - } - else { - console.log('error while uploading file to ' + url); - alert(__('Could not upload File please try again.')); - } - } - }; - - xmlhttp.open(type, url, true); - xmlhttp.setRequestHeader("Content-type", contentType); - xmlhttp.send(data); - - console.log("uploadFile end"); - }, - - /** - * Process successful response to slot request. - * - * @param {String} stanza - * @param {Function} cb - */ - successfulRequestSlotCB (stanza, cb) { - var slot = stanza.getElementsByTagName('slot')[0]; - - if (slot != undefined) { - var put = slot.getElementsByTagName('put')[0].textContent; - var get = slot.getElementsByTagName('get')[0].textContent; - cb({ - put: put, - get: get - }); - } else { - this.failedRequestSlotCB(stanza, cb); - } - }, - - /** - * Process failed response to slot request. - * - * @param {String} stanza - * @param {Function} cb - */ - failedRequestSlotCB (stanza, cb) { - chatBox.showHelpMessages([__('Fileupload failed')],'info'); - } - }) - } - }); - - return converse; -})); \ No newline at end of file +/* + The MIT License (MIT) + + Copyright (c) 2014 Klaus Herberth + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/** + * Implements Http File Upload (XEP-0363) + * + * @see {@link http://xmpp.org/extensions/xep-0363.html} + */ +(function (root, factory) { + define([ + "converse-core", + ], factory); +}(this, function ( + converse + ) { + "use strict"; + const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env; + + Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload'); + + var requestSlotUrl; + var ready; + var httpUploadOption = { + enable: true + } + + converse.plugins.add('converse-httpFileUpload', { + + dependencies: ["converse-chatboxes", "converse-disco"], + + initialize() { + const { _converse } = this, + { __ } = _converse; + var connection = _converse.connection; + var domain; + var file; + var chatBox; + + _converse.FileUpload = Backbone.NativeView.extend({ + /** + * Set up http file upload. + * + * @param {*} connection the current strophe-connection + */ + initFiletransfer () { + connection = _converse.connection; + domain = _converse.connection.domain; + + if (httpUploadOption && requestSlotUrl != undefined) { + ready = true; + return; + } + this.discoverUploadService(); + }, + + /** + * Discover upload service for http upload. + * + */ + discoverUploadService () { + var self = this; + console.log('discover http upload service'); + connection.disco.items(domain, null, function(items) { + var childs = items.getElementsByTagName('item'); + for(var i = 0; i < childs.length; i++){ + var jid = childs[i].attributes.jid.value; + if (ready) { + // abort, because we already found a service + return false; + } + self.queryItemForUploadService(jid); + } + }); + }, + + /** + * Query item for upload service. + * + * @param {String} jid of the logged-in user + * @param {Function} cb Callback on success + */ + queryItemForUploadService (jid) { + var self = this; + console.log('query ' + jid + ' for upload service'); + + connection.disco.info(jid, null, function(info) { + var httpUploadFeature; + var temp = info.getElementsByTagName('feature'); + for(var i = 0; i < temp.length; i++){ + var feature = temp[i].attributes.var; + if(feature != undefined && feature.value === Strophe.NS.HTTPUPLOAD){ + requestSlotUrl = jid; + ready = true; + self.sendFile(); + } + } + }); + }, + + /** + * Saves the file the user has picked. + * + * @param {*} file the name of the file the user has picked. + * @param {*} chatBox the chatbox from which the user initiated the file-upload + */ + setFile (file1, chatBox1){ + file = file1; + chatBox = chatBox1; + this.sendFile(); + }, + + /** + * Upload file. + * Waits till the Upload-Service is discovered and till the user has picked a file. + * + */ + sendFile () { + var self = this; + if(file === undefined){ + console.log("waiting to choose a file"); + return; + } + else if(requestSlotUrl === undefined){ + console.log("waiting for service discovery"); + return; + } + + console.log('Send file via http upload'); + chatBox.showHelpMessages([__('The file upload starts now')],'info'); + this.requestSlot(file, function(data) { + if (!data) { + // general error + console.log('Unknown error while requesting upload slot.'); + alert(__('File upload failed. Please check the log.')); + } else if (data.error) { + // specific error + console.log('The XMPP-Server return an error of the type: ' + data.error.type); + alert(__('File upload failed. Please check the log.')); + } else if (data.get && data.put) { + console.log('slot received, start upload to ' + data.put); + self.uploadFile(data.put, file, function() { + console.log(data.put); + + chatBox.onMessageSubmitted(data.put, null, file); + file = undefined; + }); + } + }); + }, + + /** + * Request upload slot from xmpp-server + * + * @param {File} file the file the user picked + * @param {Function} cb Callback after finished request + */ + requestSlot (file, cb) { + var self = this; + console.log("try sending file to: " + requestSlotUrl); + var iq = converse.env.$iq({ + to: requestSlotUrl, + type: 'get' + }).c('request', { + xmlns: Strophe.NS.HTTPUPLOAD + }).c('filename').t(file.name) + .up() + .c('size').t(file.size); + + connection.sendIQ(iq, function(stanza) { + self.successfulRequestSlotCB(stanza, cb); + }, function(stanza) { + self.failedRequestSlotCB(stanza, cb); + }); + }, + + /** + * Upload the given file to the given url. + * + * @param {String} url upload url + * @param {File} file the file the user picked + * @param {Function} success_cb callback on successful transition + */ + uploadFile (url, file, success_cb) { + console.log("uploadFile start"); + var xmlhttp = new XMLHttpRequest(); + var type = 'PUT'; + var contentType = 'application/octet-stream'; + var data = file; + var processData = false; + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == XMLHttpRequest.DONE) { + console.log("Status: " + xmlhttp.status); + if (xmlhttp.status == 200 || xmlhttp.status == 201) { + console.log('file successful uploaded'); + if (success_cb) { + success_cb(); + } + } + else { + console.log('error while uploading file to ' + url); + alert(__('Could not upload File please try again.')); + } + } + }; + + xmlhttp.open(type, url, true); + xmlhttp.setRequestHeader("Content-type", contentType); + xmlhttp.send(data); + + console.log("uploadFile end"); + }, + + /** + * Process successful response to slot request. + * + * @param {String} stanza + * @param {Function} cb + */ + successfulRequestSlotCB (stanza, cb) { + var slot = stanza.getElementsByTagName('slot')[0]; + + if (slot != undefined) { + var put = slot.getElementsByTagName('put')[0].textContent; + var get = slot.getElementsByTagName('get')[0].textContent; + cb({ + put: put, + get: get + }); + } else { + this.failedRequestSlotCB(stanza, cb); + } + }, + + /** + * Process failed response to slot request. + * + * @param {String} stanza + * @param {Function} cb + */ + failedRequestSlotCB (stanza, cb) { + chatBox.showHelpMessages([__('Fileupload failed')],'info'); + } + }) + } + }); + + return converse; +})); From 76d6b9470346cad18fcc182337b108a83cae9286 Mon Sep 17 00:00:00 2001 From: worlword Date: Wed, 4 Apr 2018 16:41:20 +0200 Subject: [PATCH 04/12] changed testcase for toolbar. now expects 3 buttons instead of 2 --- dist/converse.js | 137 +++++++++++++++++++++++------------------------ spec/chatbox.js | 2 +- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/dist/converse.js b/dist/converse.js index 9749da2ad..1cefebc35 100644 --- a/dist/converse.js +++ b/dist/converse.js @@ -46313,34 +46313,34 @@ __p += '\n'; return __p };}); -/* - The MIT License (MIT) - - Copyright (c) 2014 Klaus Herberth - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. +/* + The MIT License (MIT) + + Copyright (c) 2014 Klaus Herberth + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ -/** - * Implements Http File Upload (XEP-0363) - * - * @see {@link http://xmpp.org/extensions/xep-0363.html} +/** + * Implements Http File Upload (XEP-0363) + * + * @see {@link http://xmpp.org/extensions/xep-0363.html} */ (function (root, factory) { define('converse-httpFileUpload',["converse-core"], factory); @@ -46371,10 +46371,10 @@ return __p var file; var chatBox; _converse.FileUpload = Backbone.NativeView.extend({ - /** - * Set up http file upload. - * - * @param {*} connection the current strophe-connection + /** + * Set up http file upload. + * + * @param {*} connection the current strophe-connection */ initFiletransfer: function initFiletransfer() { connection = _converse.connection; @@ -46388,9 +46388,9 @@ return __p this.discoverUploadService(); }, - /** - * Discover upload service for http upload. - * + /** + * Discover upload service for http upload. + * */ discoverUploadService: function discoverUploadService() { var self = this; @@ -46411,11 +46411,11 @@ return __p }); }, - /** - * Query item for upload service. - * - * @param {String} jid of the logged-in user - * @param {Function} cb Callback on success + /** + * Query item for upload service. + * + * @param {String} jid of the logged-in user + * @param {Function} cb Callback on success */ queryItemForUploadService: function queryItemForUploadService(jid) { var self = this; @@ -46436,11 +46436,11 @@ return __p }); }, - /** - * Saves the file the user has picked. - * - * @param {*} file the name of the file the user has picked. - * @param {*} chatBox the chatbox from which the user initiated the file-upload + /** + * Saves the file the user has picked. + * + * @param {*} file the name of the file the user has picked. + * @param {*} chatBox the chatbox from which the user initiated the file-upload */ setFile: function setFile(file1, chatBox1) { file = file1; @@ -46448,10 +46448,10 @@ return __p this.sendFile(); }, - /** - * Upload file. - * Waits till the Upload-Service is discovered and till the user has picked a file. - * + /** + * Upload file. + * Waits till the Upload-Service is discovered and till the user has picked a file. + * */ sendFile: function sendFile() { var self = this; @@ -46486,11 +46486,11 @@ return __p }); }, - /** - * Request upload slot from xmpp-server - * - * @param {File} file the file the user picked - * @param {Function} cb Callback after finished request + /** + * Request upload slot from xmpp-server + * + * @param {File} file the file the user picked + * @param {Function} cb Callback after finished request */ requestSlot: function requestSlot(file, cb) { var self = this; @@ -46508,12 +46508,12 @@ return __p }); }, - /** - * Upload the given file to the given url. - * - * @param {String} url upload url - * @param {File} file the file the user picked - * @param {Function} success_cb callback on successful transition + /** + * Upload the given file to the given url. + * + * @param {String} url upload url + * @param {File} file the file the user picked + * @param {Function} success_cb callback on successful transition */ uploadFile: function uploadFile(url, file, success_cb) { console.log("uploadFile start"); @@ -46546,11 +46546,11 @@ return __p console.log("uploadFile end"); }, - /** - * Process successful response to slot request. - * - * @param {String} stanza - * @param {Function} cb + /** + * Process successful response to slot request. + * + * @param {String} stanza + * @param {Function} cb */ successfulRequestSlotCB: function successfulRequestSlotCB(stanza, cb) { var slot = stanza.getElementsByTagName('slot')[0]; @@ -46567,11 +46567,11 @@ return __p } }, - /** - * Process failed response to slot request. - * - * @param {String} stanza - * @param {Function} cb + /** + * Process failed response to slot request. + * + * @param {String} stanza + * @param {Function} cb */ failedRequestSlotCB: function failedRequestSlotCB(stanza, cb) { chatBox.showHelpMessages([__('Fileupload failed')], 'info'); @@ -50410,7 +50410,6 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat }); return Backbone.VDOMView; }); -//# sourceMappingURL=backbone.vdomview.js.map; (function (root, factory) { if (typeof define === 'function' && define.amd) { define('converse-modal',["converse-core", "bootstrap", "underscore", "backbone", "backbone.vdomview"], factory); diff --git a/spec/chatbox.js b/spec/chatbox.js index bd6a13669..9b2c19bfa 100644 --- a/spec/chatbox.js +++ b/spec/chatbox.js @@ -416,7 +416,7 @@ expect(view).toBeDefined(); var $toolbar = $(view.el).find('ul.chat-toolbar'); expect($toolbar.length).toBe(1); - expect($toolbar.children('li').length).toBe(2); + expect($toolbar.children('li').length).toBe(3); done(); })); From 7839f2084628d169c7e39711d79fb32a62ea2bf4 Mon Sep 17 00:00:00 2001 From: worlword Date: Thu, 5 Apr 2018 08:45:58 +0200 Subject: [PATCH 05/12] fixed sendMessage for so that tests will work again --- src/converse-chatview.js | 20 ++++++++++---------- src/converse-muc-views.js | 4 +++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/converse-chatview.js b/src/converse-chatview.js index f7e36e12f..344e9b54f 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -813,7 +813,7 @@ return stanza; }, - sendMessage (message) { + sendMessage (message, file=null) { /* Responsible for sending off a text message. * * Parameters: @@ -821,7 +821,14 @@ */ // TODO: We might want to send to specfic resources. // Especially in the OTR case. - _converse.connection.send(message); + var messageStanza; + if(file !== null){ + messageStanza = this.createFileMessageStanza(message); + } + else { + messageStanza = this.createMessageStanza(message); + } + _converse.connection.send(messageStanza); if (_converse.forward_messages) { // Forward the message, so that other connected resources are also aware of it. _converse.connection.send( @@ -878,14 +885,7 @@ const message = this.model.messages.create(attrs); /* check, if a file was send. If true it will send the file with XEP-0066. */ - var messageStanza; - if(file !== null){ - messageStanza = this.createFileMessageStanza(message); - } - else { - messageStanza = this.createMessageStanza(message); - } - this.sendMessage(messageStanza); + this.sendMessage(message, file); }, getOutgoingMessageAttributes (text, spoiler_hint) { diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index d35b99005..d70385a57 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -747,7 +747,9 @@ this.showStatusNotification(__("Error: could not execute the command"), true); }, - onMessageSubmitted (text, notNeeded, file = null) { + // the notNeeded-Parameter is there so this method has the same amount of parameters as converse-chatview.js->onMessageSubmitted + // this allows to call the same method from diffrent plugins + onMessageSubmitted (text, notNeeded = null, file = null) { /* Gets called when the user presses enter to send off a * message in a chat room. * From 258029457fe9083b1962eddf5d07b22a41262f1d Mon Sep 17 00:00:00 2001 From: worlword Date: Thu, 5 Apr 2018 08:48:20 +0200 Subject: [PATCH 06/12] Removed dist-files from pull request --- dist/converse.js | 1491 +++++++++++++++++----------------------------- 1 file changed, 539 insertions(+), 952 deletions(-) diff --git a/dist/converse.js b/dist/converse.js index 1cefebc35..26213aa55 100644 --- a/dist/converse.js +++ b/dist/converse.js @@ -28230,73 +28230,6 @@ return es; }))); -//! moment.js locale configuration -//! locale : Basque [eu] -//! author : Eneko Illarramendi : https://github.com/eillarra - -;(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' - && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define('moment/locale/eu',['../moment'], factory) : - factory(global.moment) -}(this, (function (moment) { 'use strict'; - - -var eu = moment.defineLocale('eu', { - months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'), - monthsShort : 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'), - monthsParseExact : true, - weekdays : 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'), - weekdaysShort : 'ig._al._ar._az._og._ol._lr.'.split('_'), - weekdaysMin : 'ig_al_ar_az_og_ol_lr'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'YYYY[ko] MMMM[ren] D[a]', - LLL : 'YYYY[ko] MMMM[ren] D[a] HH:mm', - LLLL : 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm', - l : 'YYYY-M-D', - ll : 'YYYY[ko] MMM D[a]', - lll : 'YYYY[ko] MMM D[a] HH:mm', - llll : 'ddd, YYYY[ko] MMM D[a] HH:mm' - }, - calendar : { - sameDay : '[gaur] LT[etan]', - nextDay : '[bihar] LT[etan]', - nextWeek : 'dddd LT[etan]', - lastDay : '[atzo] LT[etan]', - lastWeek : '[aurreko] dddd LT[etan]', - sameElse : 'L' - }, - relativeTime : { - future : '%s barru', - past : 'duela %s', - s : 'segundo batzuk', - m : 'minutu bat', - mm : '%d minutu', - h : 'ordu bat', - hh : '%d ordu', - d : 'egun bat', - dd : '%d egun', - M : 'hilabete bat', - MM : '%d hilabete', - y : 'urte bat', - yy : '%d urte' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } -}); - -return eu; - -}))); - //! moment.js locale configuration //! locale : French [fr] //! author : John Fischer : https://github.com/jfroffice @@ -29811,6 +29744,8 @@ return zhTw; }))); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -29822,8 +29757,8 @@ return zhTw; /*global define */ (function (root, factory) { - define('i18n',["es6-promise", "jed", "lodash.noconflict", "moment", 'moment/locale/af', 'moment/locale/ar', 'moment/locale/bg', 'moment/locale/ca', 'moment/locale/de', 'moment/locale/es', 'moment/locale/eu', 'moment/locale/fr', 'moment/locale/he', 'moment/locale/hu', 'moment/locale/id', 'moment/locale/it', 'moment/locale/ja', 'moment/locale/nb', 'moment/locale/nl', 'moment/locale/pl', 'moment/locale/pt-br', 'moment/locale/ru', 'moment/locale/tr', 'moment/locale/uk', 'moment/locale/zh-cn', 'moment/locale/zh-tw'], factory); -})(this, function (Promise, Jed, _, moment) { + define('i18n',["es6-promise", "jed", "lodash.noconflict", "moment", 'moment/locale/af', 'moment/locale/ar', 'moment/locale/bg', 'moment/locale/ca', 'moment/locale/de', 'moment/locale/es', 'moment/locale/fr', 'moment/locale/he', 'moment/locale/hu', 'moment/locale/id', 'moment/locale/it', 'moment/locale/ja', 'moment/locale/nb', 'moment/locale/nl', 'moment/locale/pl', 'moment/locale/pt-br', 'moment/locale/ru', 'moment/locale/tr', 'moment/locale/uk', 'moment/locale/zh-cn', 'moment/locale/zh-tw'], factory); +})(void 0, function (Promise, Jed, _, moment) { 'use strict'; function detectLocale(library_check) { @@ -36297,6 +36232,8 @@ require(["strophe-polyfill"]); })); /* jshint ignore:end */ ; + + function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } // Converse.js (A browser based XMPP chat client) @@ -36311,7 +36248,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat /*global define, escape, window */ (function (root, factory) { define('utils',["sizzle", "es6-promise", "lodash.noconflict", "strophe"], factory); -})(this, function (sizzle, Promise, _, Strophe) { +})(void 0, function (sizzle, Promise, _, Strophe) { "use strict"; var b64_sha1 = Strophe.SHA1.b64_sha1; @@ -36526,14 +36463,6 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat })); }; - u.renderMovieURLs = function (obj) { - return ""; - }; - - u.renderAudioURLs = function (obj) { - return ""; - }; - u.slideInAllElements = function (elements) { var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 300; return Promise.all(_.map(elements, _.partial(u.slideIn, _, duration))); @@ -37048,19 +36977,9 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat el.dispatchEvent(evt); }; - u.geoUriToHttp = function (text, _converse) { - var regex = /geo:([\-0-9.]+),([\-0-9.]+)(?:,([\-0-9.]+))?(?:\?(.*))?/g; - return text.replace(regex, _converse.geouri_replacement); - }; - - u.httpToGeoUri = function (text, _converse) { - var replacement = 'geo:$1,$2'; - return text.replace(_converse.geouri_regex, replacement); - }; - return u; }); -//# sourceMappingURL=core.js.map; +//# sourceMappingURL=utils.js.map; (function (global, factory) { if (typeof define === "function" && define.amd) { define('pluggable',['exports', 'lodash'], factory); @@ -39728,6 +39647,8 @@ Backbone.sync = function(method, model, options) { return Backbone.BrowserStorage; })); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -39738,7 +39659,7 @@ return Backbone.BrowserStorage; /*global Backbone, define, window, JSON */ (function (root, factory) { define('converse-core',["sizzle", "es6-promise", "lodash.noconflict", "lodash.fp", "polyfill", "i18n", "utils", "moment", "strophe", "pluggable", "backbone.noconflict", "backbone.nativeview", "backbone.browserStorage"], factory); -})(this, function (sizzle, Promise, _, f, polyfill, i18n, u, moment, Strophe, pluggable, Backbone) { +})(void 0, function (sizzle, Promise, _, f, polyfill, i18n, u, moment, Strophe, pluggable, Backbone) { /* Cannot use this due to Safari bug. * See https://github.com/jcbrand/converse.js/issues/196 */ @@ -39787,7 +39708,7 @@ return Backbone.BrowserStorage; _.extend(_converse, Backbone.Events); // Core plugins are whitelisted automatically - _converse.core_plugins = ['converse-bookmarks', 'converse-chatboxes', 'converse-chatview', 'converse-controlbox', 'converse-core', 'converse-disco', 'converse-dragresize', 'converse-dropdown', 'converse-fullscreen', 'converse-headline', 'converse-httpFileUpload', 'converse-mam', 'converse-minimize', 'converse-modal', 'converse-muc', 'converse-muc-embedded', 'converse-muc-views', 'converse-notification', 'converse-otr', 'converse-ping', 'converse-profile', 'converse-register', 'converse-roomslist', 'converse-rosterview', 'converse-singleton', 'converse-spoilers', 'converse-vcard']; // Make converse pluggable + _converse.core_plugins = ['converse-bookmarks', 'converse-chatboxes', 'converse-chatview', 'converse-controlbox', 'converse-core', 'converse-disco', 'converse-dragresize', 'converse-dropdown', 'converse-fullscreen', 'converse-headline', 'converse-mam', 'converse-minimize', 'converse-modal', 'converse-muc', 'converse-muc-embedded', 'converse-muc-views', 'converse-notification', 'converse-otr', 'converse-ping', 'converse-profile', 'converse-register', 'converse-roomslist', 'converse-rosterview', 'converse-singleton', 'converse-spoilers', 'converse-vcard']; // Make converse pluggable pluggable.enable(_converse, '_converse', 'pluggable'); // Module-level constants @@ -40012,14 +39933,12 @@ return Backbone.BrowserStorage; expose_rid_and_sid: false, filter_by_resource: false, forward_messages: false, - geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g, - geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2', hide_offline_users: false, include_offline_state: false, jid: undefined, keepalive: true, locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json', - locales: ['af', 'ar', 'bg', 'ca', 'de', 'es', 'eu', 'en', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'nb', 'nl', 'pl', 'pt_BR', 'ru', 'tr', 'uk', 'zh_CN', 'zh_TW'], + locales: ['af', 'ar', 'bg', 'ca', 'de', 'es', 'en', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'nb', 'nl', 'pl', 'pt_BR', 'ru', 'tr', 'uk', 'zh_CN', 'zh_TW'], message_carbons: true, message_storage: 'session', nickname: undefined, @@ -40829,7 +40748,7 @@ return Backbone.BrowserStorage; } } }, - removeFromRoster: function removeFromRoster(callback, errback) { + removeFromRoster: function removeFromRoster(callback) { /* Instruct the XMPP server to remove this contact from our roster * Parameters: * (Function) callback @@ -40843,7 +40762,7 @@ return Backbone.BrowserStorage; subscription: "remove" }); - _converse.connection.sendIQ(iq, callback, errback); + _converse.connection.sendIQ(iq, callback, callback); return this; } @@ -46297,12 +46216,6 @@ function print() { __p += __j.call(arguments, '') } if (o.use_emoji) { ; __p += '\n
  • \n \n
  • \n'; } ; -__p += '\n'; - if (o.show_fileUpload_button) { ; -__p += '\n\n
  • \n \n
  • \n'; - } ; __p += '\n'; if (o.show_call_button) { ; __p += '\n
  • 2 && arguments[2] !== undefined ? arguments[2] : null; - /* This method gets called once the user has typed a message * and then pressed enter in a chat box. * @@ -48155,17 +47763,7 @@ return __p var attrs = this.getOutgoingMessageAttributes(text, spoiler_hint); var message = this.model.messages.create(attrs); - /* check, if a file was send. If true it will send the file with XEP-0066. */ - - var messageStanza; - - if (file !== null) { - messageStanza = this.createFileMessageStanza(message); - } else { - messageStanza = this.createMessageStanza(message); - } - - this.sendMessage(messageStanza); + this.sendMessage(message); }, getOutgoingMessageAttributes: function getOutgoingMessageAttributes(text, spoiler_hint) { /* Overridable method which returns the attributes to be @@ -48177,7 +47775,7 @@ return __p 'fullname': _.isEmpty(fullname) ? _converse.bare_jid : fullname, 'sender': 'me', 'time': moment().format(), - 'message': u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse), + 'message': emojione.shortnameToUnicode(text), 'is_spoiler': is_spoiler }; @@ -50328,6 +49926,8 @@ exports.default = vnode; }); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy8ucmVnaXN0cnkubnBtanMub3JnL2Jyb3dzZXItcGFjay82LjAuMi9ub2RlX21vZHVsZXMvYnJvd3Nlci1wYWNrL19wcmVsdWRlLmpzIiwiaHRtbGRvbWFwaS5qcyIsInRvdm5vZGUuanMiLCJ2bm9kZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5mdW5jdGlvbiBjcmVhdGVFbGVtZW50KHRhZ05hbWUpIHtcbiAgICByZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0YWdOYW1lKTtcbn1cbmZ1bmN0aW9uIGNyZWF0ZUVsZW1lbnROUyhuYW1lc3BhY2VVUkksIHF1YWxpZmllZE5hbWUpIHtcbiAgICByZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKG5hbWVzcGFjZVVSSSwgcXVhbGlmaWVkTmFtZSk7XG59XG5mdW5jdGlvbiBjcmVhdGVUZXh0Tm9kZSh0ZXh0KSB7XG4gICAgcmV0dXJuIGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRleHQpO1xufVxuZnVuY3Rpb24gY3JlYXRlQ29tbWVudCh0ZXh0KSB7XG4gICAgcmV0dXJuIGRvY3VtZW50LmNyZWF0ZUNvbW1lbnQodGV4dCk7XG59XG5mdW5jdGlvbiBpbnNlcnRCZWZvcmUocGFyZW50Tm9kZSwgbmV3Tm9kZSwgcmVmZXJlbmNlTm9kZSkge1xuICAgIHBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKG5ld05vZGUsIHJlZmVyZW5jZU5vZGUpO1xufVxuZnVuY3Rpb24gcmVtb3ZlQ2hpbGQobm9kZSwgY2hpbGQpIHtcbiAgICBub2RlLnJlbW92ZUNoaWxkKGNoaWxkKTtcbn1cbmZ1bmN0aW9uIGFwcGVuZENoaWxkKG5vZGUsIGNoaWxkKSB7XG4gICAgbm9kZS5hcHBlbmRDaGlsZChjaGlsZCk7XG59XG5mdW5jdGlvbiBwYXJlbnROb2RlKG5vZGUpIHtcbiAgICByZXR1cm4gbm9kZS5wYXJlbnROb2RlO1xufVxuZnVuY3Rpb24gbmV4dFNpYmxpbmcobm9kZSkge1xuICAgIHJldHVybiBub2RlLm5leHRTaWJsaW5nO1xufVxuZnVuY3Rpb24gdGFnTmFtZShlbG0pIHtcbiAgICByZXR1cm4gZWxtLnRhZ05hbWU7XG59XG5mdW5jdGlvbiBzZXRUZXh0Q29udGVudChub2RlLCB0ZXh0KSB7XG4gICAgbm9kZS50ZXh0Q29udGVudCA9IHRleHQ7XG59XG5mdW5jdGlvbiBnZXRUZXh0Q29udGVudChub2RlKSB7XG4gICAgcmV0dXJuIG5vZGUudGV4dENvbnRlbnQ7XG59XG5mdW5jdGlvbiBpc0VsZW1lbnQobm9kZSkge1xuICAgIHJldHVybiBub2RlLm5vZGVUeXBlID09PSAxO1xufVxuZnVuY3Rpb24gaXNUZXh0KG5vZGUpIHtcbiAgICByZXR1cm4gbm9kZS5ub2RlVHlwZSA9PT0gMztcbn1cbmZ1bmN0aW9uIGlzQ29tbWVudChub2RlKSB7XG4gICAgcmV0dXJuIG5vZGUubm9kZVR5cGUgPT09IDg7XG59XG5leHBvcnRzLmh0bWxEb21BcGkgPSB7XG4gICAgY3JlYXRlRWxlbWVudDogY3JlYXRlRWxlbWVudCxcbiAgICBjcmVhdGVFbGVtZW50TlM6IGNyZWF0ZUVsZW1lbnROUyxcbiAgICBjcmVhdGVUZXh0Tm9kZTogY3JlYXRlVGV4dE5vZGUsXG4gICAgY3JlYXRlQ29tbWVudDogY3JlYXRlQ29tbWVudCxcbiAgICBpbnNlcnRCZWZvcmU6IGluc2VydEJlZm9yZSxcbiAgICByZW1vdmVDaGlsZDogcmVtb3ZlQ2hpbGQsXG4gICAgYXBwZW5kQ2hpbGQ6IGFwcGVuZENoaWxkLFxuICAgIHBhcmVudE5vZGU6IHBhcmVudE5vZGUsXG4gICAgbmV4dFNpYmxpbmc6IG5leHRTaWJsaW5nLFxuICAgIHRhZ05hbWU6IHRhZ05hbWUsXG4gICAgc2V0VGV4dENvbnRlbnQ6IHNldFRleHRDb250ZW50LFxuICAgIGdldFRleHRDb250ZW50OiBnZXRUZXh0Q29udGVudCxcbiAgICBpc0VsZW1lbnQ6IGlzRWxlbWVudCxcbiAgICBpc1RleHQ6IGlzVGV4dCxcbiAgICBpc0NvbW1lbnQ6IGlzQ29tbWVudCxcbn07XG5leHBvcnRzLmRlZmF1bHQgPSBleHBvcnRzLmh0bWxEb21BcGk7XG4vLyMgc291cmNlTWFwcGluZ1VSTD1odG1sZG9tYXBpLmpzLm1hcCIsIlwidXNlIHN0cmljdFwiO1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xudmFyIHZub2RlXzEgPSByZXF1aXJlKFwiLi92bm9kZVwiKTtcbnZhciBodG1sZG9tYXBpXzEgPSByZXF1aXJlKFwiLi9odG1sZG9tYXBpXCIpO1xuZnVuY3Rpb24gdG9WTm9kZShub2RlLCBkb21BcGkpIHtcbiAgICB2YXIgYXBpID0gZG9tQXBpICE9PSB1bmRlZmluZWQgPyBkb21BcGkgOiBodG1sZG9tYXBpXzEuZGVmYXVsdDtcbiAgICB2YXIgdGV4dDtcbiAgICBpZiAoYXBpLmlzRWxlbWVudChub2RlKSkge1xuICAgICAgICB2YXIgaWQgPSBub2RlLmlkID8gJyMnICsgbm9kZS5pZCA6ICcnO1xuICAgICAgICB2YXIgY24gPSBub2RlLmdldEF0dHJpYnV0ZSgnY2xhc3MnKTtcbiAgICAgICAgdmFyIGMgPSBjbiA/ICcuJyArIGNuLnNwbGl0KCcgJykuam9pbignLicpIDogJyc7XG4gICAgICAgIHZhciBzZWwgPSBhcGkudGFnTmFtZShub2RlKS50b0xvd2VyQ2FzZSgpICsgaWQgKyBjO1xuICAgICAgICB2YXIgYXR0cnMgPSB7fTtcbiAgICAgICAgdmFyIGNoaWxkcmVuID0gW107XG4gICAgICAgIHZhciBuYW1lXzE7XG4gICAgICAgIHZhciBpID0gdm9pZCAwLCBuID0gdm9pZCAwO1xuICAgICAgICB2YXIgZWxtQXR0cnMgPSBub2RlLmF0dHJpYnV0ZXM7XG4gICAgICAgIHZhciBlbG1DaGlsZHJlbiA9IG5vZGUuY2hpbGROb2RlcztcbiAgICAgICAgZm9yIChpID0gMCwgbiA9IGVsbUF0dHJzLmxlbmd0aDsgaSA8IG47IGkrKykge1xuICAgICAgICAgICAgbmFtZV8xID0gZWxtQXR0cnNbaV0ubm9kZU5hbWU7XG4gICAgICAgICAgICBpZiAobmFtZV8xICE9PSAnaWQnICYmIG5hbWVfMSAhPT0gJ2NsYXNzJykge1xuICAgICAgICAgICAgICAgIGF0dHJzW25hbWVfMV0gPSBlbG1BdHRyc1tpXS5ub2RlVmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChpID0gMCwgbiA9IGVsbUNoaWxkcmVuLmxlbmd0aDsgaSA8IG47IGkrKykge1xuICAgICAgICAgICAgY2hpbGRyZW4ucHVzaCh0b1ZOb2RlKGVsbUNoaWxkcmVuW2ldKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHZub2RlXzEuZGVmYXVsdChzZWwsIHsgYXR0cnM6IGF0dHJzIH0sIGNoaWxkcmVuLCB1bmRlZmluZWQsIG5vZGUpO1xuICAgIH1cbiAgICBlbHNlIGlmIChhcGkuaXNUZXh0KG5vZGUpKSB7XG4gICAgICAgIHRleHQgPSBhcGkuZ2V0VGV4dENvbnRlbnQobm9kZSk7XG4gICAgICAgIHJldHVybiB2bm9kZV8xLmRlZmF1bHQodW5kZWZpbmVkLCB1bmRlZmluZWQsIHVuZGVmaW5lZCwgdGV4dCwgbm9kZSk7XG4gICAgfVxuICAgIGVsc2UgaWYgKGFwaS5pc0NvbW1lbnQobm9kZSkpIHtcbiAgICAgICAgdGV4dCA9IGFwaS5nZXRUZXh0Q29udGVudChub2RlKTtcbiAgICAgICAgcmV0dXJuIHZub2RlXzEuZGVmYXVsdCgnIScsIHt9LCBbXSwgdGV4dCwgbm9kZSk7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICByZXR1cm4gdm5vZGVfMS5kZWZhdWx0KCcnLCB7fSwgW10sIHVuZGVmaW5lZCwgdW5kZWZpbmVkKTtcbiAgICB9XG59XG5leHBvcnRzLnRvVk5vZGUgPSB0b1ZOb2RlO1xuZXhwb3J0cy5kZWZhdWx0ID0gdG9WTm9kZTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPXRvdm5vZGUuanMubWFwIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5mdW5jdGlvbiB2bm9kZShzZWwsIGRhdGEsIGNoaWxkcmVuLCB0ZXh0LCBlbG0pIHtcbiAgICB2YXIga2V5ID0gZGF0YSA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogZGF0YS5rZXk7XG4gICAgcmV0dXJuIHsgc2VsOiBzZWwsIGRhdGE6IGRhdGEsIGNoaWxkcmVuOiBjaGlsZHJlbixcbiAgICAgICAgdGV4dDogdGV4dCwgZWxtOiBlbG0sIGtleToga2V5IH07XG59XG5leHBvcnRzLnZub2RlID0gdm5vZGU7XG5leHBvcnRzLmRlZmF1bHQgPSB2bm9kZTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPXZub2RlLmpzLm1hcCJdfQ== ; + + function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } /*! @@ -50342,7 +49942,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat // CommonJS-like environments module.exports = factory(require('snabbdom'), require('snabbdom-attributes'), require('snabbdom-class'), require('snabbdom-dataset'), require('snabbdom-props'), require('snabbdom-style'), require('tovnode'), require('underscore'), require('backbone')); } -})(this, function (snabbdom, snabbdom_attributes, snabbdom_class, snabbdom_dataset, snabbdom_props, snabbdom_style, tovnode, _, Backbone) { +})(void 0, function (snabbdom, snabbdom_attributes, snabbdom_class, snabbdom_dataset, snabbdom_props, snabbdom_style, tovnode, _, Backbone) { "use strict"; var domParser = new DOMParser(); @@ -50410,11 +50010,14 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat }); return Backbone.VDOMView; }); +//# sourceMappingURL=backbone.vdomview.js.map; + + (function (root, factory) { if (typeof define === 'function' && define.amd) { define('converse-modal',["converse-core", "bootstrap", "underscore", "backbone", "backbone.vdomview"], factory); } -})(this, function (converse, bootstrap, _, Backbone) { +})(void 0, function (converse, bootstrap, _, Backbone) { "use strict"; converse.plugins.add('converse-modal', { @@ -50451,6 +50054,8 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat }); }); //# sourceMappingURL=converse-modal.js.map; + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -50461,7 +50066,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat /*global define */ (function (root, factory) { define('converse-rosterview',["converse-core", "tpl!add_contact_modal", "tpl!group_header", "tpl!pending_contact", "tpl!requesting_contact", "tpl!roster", "tpl!roster_filter", "tpl!roster_item", "tpl!search_contact", "awesomplete", "converse-chatboxes", "converse-modal"], factory); -})(this, function (converse, tpl_add_contact_modal, tpl_group_header, tpl_pending_contact, tpl_requesting_contact, tpl_roster, tpl_roster_filter, tpl_roster_item, tpl_search_contact, Awesomplete) { +})(void 0, function (converse, tpl_add_contact_modal, tpl_group_header, tpl_pending_contact, tpl_requesting_contact, tpl_roster, tpl_roster_filter, tpl_roster_item, tpl_search_contact, Awesomplete) { "use strict"; var _converse$env = converse.env, @@ -50943,7 +50548,16 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat var result = confirm(__("Are you sure you want to remove this contact?")); if (result === true) { - this.model.removeFromRoster(function (iq) { + var iq = $iq({ + type: 'set' + }).c('query', { + xmlns: Strophe.NS.ROSTER + }).c('item', { + jid: this.model.get('jid'), + subscription: "remove" + }); + + _converse.connection.sendIQ(iq, function (iq) { _this.model.destroy(); _this.remove(); @@ -51709,6 +51323,8 @@ return __p }); })); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -51719,7 +51335,7 @@ return __p /*global define */ (function (root, factory) { define('converse-vcard',["converse-core", "strophe.vcard"], factory); -})(this, function (converse) { +})(void 0, function (converse) { "use strict"; var _converse$env = converse.env, @@ -51931,6 +51547,8 @@ return __p }); }); //# sourceMappingURL=converse-vcard.js.map; + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -51941,7 +51559,7 @@ return __p /*global define */ (function (root, factory) { define('converse-profile',["converse-core", "bootstrap", "tpl!chat_status_modal", "tpl!profile_modal", "tpl!profile_view", "tpl!status_option", "converse-vcard", "converse-modal"], factory); -})(this, function (converse, bootstrap, tpl_chat_status_modal, tpl_profile_modal, tpl_profile_view, tpl_status_option) { +})(void 0, function (converse, bootstrap, tpl_chat_status_modal, tpl_profile_modal, tpl_profile_view, tpl_status_option) { "use strict"; var _converse$env = converse.env, @@ -52083,6 +51701,8 @@ return __p }); }); //# sourceMappingURL=converse-profile.js.map; + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -52093,7 +51713,7 @@ return __p /*global define */ (function (root, factory) { define('converse-controlbox',["converse-core", "lodash.fp", "tpl!converse_brand_heading", "tpl!controlbox", "tpl!controlbox_toggle", "tpl!login_panel", "converse-chatview", "converse-rosterview", "converse-profile"], factory); -})(this, function (converse, fp, tpl_brand_heading, tpl_controlbox, tpl_controlbox_toggle, tpl_login_panel) { +})(void 0, function (converse, fp, tpl_brand_heading, tpl_controlbox, tpl_controlbox_toggle, tpl_login_panel) { "use strict"; var CHATBOX_TYPE = 'chatbox'; @@ -52865,6 +52485,8 @@ __e(o.value) + return __p };}); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -52877,7 +52499,7 @@ return __p /*global define, escape, Jed */ (function (root, factory) { define('form-utils',["sizzle", "lodash.noconflict", "utils", "tpl!field", "tpl!select_option", "tpl!form_select", "tpl!form_textarea", "tpl!form_checkbox", "tpl!form_username", "tpl!form_input", "tpl!form_captcha", "tpl!form_url"], factory); -})(this, function (sizzle, _, u, tpl_field, tpl_select_option, tpl_form_select, tpl_form_textarea, tpl_form_checkbox, tpl_form_username, tpl_form_input, tpl_form_captcha, tpl_form_url) { +})(void 0, function (sizzle, _, u, tpl_field, tpl_select_option, tpl_form_select, tpl_form_textarea, tpl_form_checkbox, tpl_form_username, tpl_form_input, tpl_form_captcha, tpl_form_url) { "use strict"; var XFORM_TYPE_MAP = { @@ -53006,7 +52628,7 @@ return __p return u; }); -//# sourceMappingURL=form.js.map; +//# sourceMappingURL=form-utils.js.map; /* Copyright 2010, François de Metz */ @@ -53271,6 +52893,8 @@ Strophe.addConnectionPlugin('disco', }); })); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -53283,7 +52907,7 @@ Strophe.addConnectionPlugin('disco', /*global Backbone, define, window */ (function (root, factory) { define('converse-disco',["converse-core", "sizzle", "strophe.disco"], factory); -})(this, function (converse, sizzle) { +})(void 0, function (converse, sizzle) { var _converse$env = converse.env, Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, @@ -53688,112 +53312,8 @@ Strophe.addConnectionPlugin('disco', }); //# sourceMappingURL=backbone.orderedlistview.js.map; -// Converse.js (A browser based XMPP chat client) -// http://conversejs.org -// -// This is the utilities module. -// -// Copyright (c) 2012-2017, Jan-Carel Brand -// Licensed under the Mozilla Public License (MPLv2) -// - -/*global define, escape, Jed */ -(function (root, factory) { - define('muc-utils',["converse-core", "utils"], factory); -})(this, function (converse, u) { - "use strict"; - - var _converse$env = converse.env, - Strophe = _converse$env.Strophe, - sizzle = _converse$env.sizzle, - _ = _converse$env._; - - u.computeAffiliationsDelta = function computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list) { - /* Given two lists of objects with 'jid', 'affiliation' and - * 'reason' properties, return a new list containing - * those objects that are new, changed or removed - * (depending on the 'remove_absentees' boolean). - * - * The affiliations for new and changed members stay the - * same, for removed members, the affiliation is set to 'none'. - * - * The 'reason' property is not taken into account when - * comparing whether affiliations have been changed. - * - * Parameters: - * (Boolean) exclude_existing: Indicates whether JIDs from - * the new list which are also in the old list - * (regardless of affiliation) should be excluded - * from the delta. One reason to do this - * would be when you want to add a JID only if it - * doesn't have *any* existing affiliation at all. - * (Boolean) remove_absentees: Indicates whether JIDs - * from the old list which are not in the new list - * should be considered removed and therefore be - * included in the delta with affiliation set - * to 'none'. - * (Array) new_list: Array containing the new affiliations - * (Array) old_list: Array containing the old affiliations - */ - var new_jids = _.map(new_list, 'jid'); - - var old_jids = _.map(old_list, 'jid'); // Get the new affiliations - var delta = _.map(_.difference(new_jids, old_jids), function (jid) { - return new_list[_.indexOf(new_jids, jid)]; - }); - - if (!exclude_existing) { - // Get the changed affiliations - delta = delta.concat(_.filter(new_list, function (item) { - var idx = _.indexOf(old_jids, item.jid); - - if (idx >= 0) { - return item.affiliation !== old_list[idx].affiliation; - } - - return false; - })); - } - - if (remove_absentees) { - // Get the removed affiliations - delta = delta.concat(_.map(_.difference(old_jids, new_jids), function (jid) { - return { - 'jid': jid, - 'affiliation': 'none' - }; - })); - } - - return delta; - }; - - u.parseMemberListIQ = function parseMemberListIQ(iq) { - /* Given an IQ stanza with a member list, create an array of member - * objects. - */ - return _.map(sizzle("query[xmlns=\"".concat(Strophe.NS.MUC_ADMIN, "\"] item"), iq), function (item) { - return { - 'jid': item.getAttribute('jid'), - 'affiliation': item.getAttribute('affiliation') - }; - }); - }; - - u.marshallAffiliationIQs = function marshallAffiliationIQs() { - /* Marshall a list of IQ stanzas into a map of JIDs and - * affiliations. - * - * Parameters: - * Any amount of XMLElement objects, representing the IQ - * stanzas. - */ - return _.flatMap(arguments[0], u.parseMemberListIQ); - }; -}); -//# sourceMappingURL=muc.js.map; // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -53807,8 +53327,8 @@ Strophe.addConnectionPlugin('disco', * specified in XEP-0045 Multi-user chat. */ (function (root, factory) { - define('converse-muc',["form-utils", "converse-core", "converse-chatview", "converse-disco", "backbone.overview", "backbone.orderedlistview", "backbone.vdomview", "muc-utils"], factory); -})(this, function (u, converse) { + define('converse-muc',["form-utils", "converse-core", "converse-chatview", "converse-disco", "backbone.overview", "backbone.orderedlistview", "backbone.vdomview"], factory); +})(void 0, function (u, converse) { "use strict"; var MUC_ROLE_WEIGHTS = { @@ -54056,328 +53576,6 @@ Strophe.addConnectionPlugin('disco', 'type': converse.CHATROOMS_TYPE }); }, - directInvite: function directInvite(recipient, reason) { - /* Send a direct invitation as per XEP-0249 - * - * Parameters: - * (String) recipient - JID of the person being invited - * (String) reason - Optional reason for the invitation - */ - if (this.get('membersonly')) { - // When inviting to a members-only room, we first add - // the person to the member list by giving them an - // affiliation of 'member' (if they're not affiliated - // already), otherwise they won't be able to join. - var map = {}; - map[recipient] = 'member'; - - var deltaFunc = _.partial(u.computeAffiliationsDelta, true, false); - - this.updateMemberLists([{ - 'jid': recipient, - 'affiliation': 'member', - 'reason': reason - }], ['member', 'owner', 'admin'], deltaFunc); - } - - var attrs = { - 'xmlns': 'jabber:x:conference', - 'jid': this.get('jid') - }; - - if (reason !== null) { - attrs.reason = reason; - } - - if (this.get('password')) { - attrs.password = this.get('password'); - } - - var invitation = $msg({ - from: _converse.connection.jid, - to: recipient, - id: _converse.connection.getUniqueId() - }).c('x', attrs); - - _converse.connection.send(invitation); - - _converse.emit('roomInviteSent', { - 'room': this, - 'recipient': recipient, - 'reason': reason - }); - }, - sendConfiguration: function sendConfiguration(config, callback, errback) { - /* Send an IQ stanza with the room configuration. - * - * Parameters: - * (Array) config: The room configuration - * (Function) callback: Callback upon succesful IQ response - * The first parameter passed in is IQ containing the - * room configuration. - * The second is the response IQ from the server. - * (Function) errback: Callback upon error IQ response - * The first parameter passed in is IQ containing the - * room configuration. - * The second is the response IQ from the server. - */ - var iq = $iq({ - to: this.get('jid'), - type: "set" - }).c("query", { - xmlns: Strophe.NS.MUC_OWNER - }).c("x", { - xmlns: Strophe.NS.XFORM, - type: "submit" - }); - - _.each(config || [], function (node) { - iq.cnode(node).up(); - }); - - callback = _.isUndefined(callback) ? _.noop : _.partial(callback, iq.nodeTree); - errback = _.isUndefined(errback) ? _.noop : _.partial(errback, iq.nodeTree); - return _converse.connection.sendIQ(iq, callback, errback); - }, - parseRoomFeatures: function parseRoomFeatures(iq) { - /* Parses an IQ stanza containing the room's features. - * - * See http://xmpp.org/extensions/xep-0045.html#disco-roominfo - * - * - * - * - * - * - * - * - * - * - */ - var features = { - 'features_fetched': true, - 'name': iq.querySelector('identity').getAttribute('name') - }; - - _.each(iq.querySelectorAll('feature'), function (field) { - var fieldname = field.getAttribute('var'); - - if (!fieldname.startsWith('muc_')) { - if (fieldname === Strophe.NS.MAM) { - features.mam_enabled = true; - } - - return; - } - - features[fieldname.replace('muc_', '')] = true; - }); - - var desc_field = iq.querySelector('field[var="muc#roominfo_description"] value'); - - if (!_.isNull(desc_field)) { - features.description = desc_field.textContent; - } - - this.save(features); - }, - requestMemberList: function requestMemberList(affiliation) { - var _this = this; - - /* Send an IQ stanza to the server, asking it for the - * member-list of this room. - * - * See: http://xmpp.org/extensions/xep-0045.html#modifymember - * - * Parameters: - * (String) affiliation: The specific member list to - * fetch. 'admin', 'owner' or 'member'. - * - * Returns: - * A promise which resolves once the list has been - * retrieved. - */ - return new Promise(function (resolve, reject) { - affiliation = affiliation || 'member'; - var iq = $iq({ - to: _this.get('jid'), - type: "get" - }).c("query", { - xmlns: Strophe.NS.MUC_ADMIN - }).c("item", { - 'affiliation': affiliation - }); - - _converse.connection.sendIQ(iq, resolve, reject); - }); - }, - setAffiliation: function setAffiliation(affiliation, members) { - /* Send IQ stanzas to the server to set an affiliation for - * the provided JIDs. - * - * See: http://xmpp.org/extensions/xep-0045.html#modifymember - * - * XXX: Prosody doesn't accept multiple JIDs' affiliations - * being set in one IQ stanza, so as a workaround we send - * a separate stanza for each JID. - * Related ticket: https://prosody.im/issues/issue/795 - * - * Parameters: - * (String) affiliation: The affiliation - * (Object) members: A map of jids, affiliations and - * optionally reasons. Only those entries with the - * same affiliation as being currently set will be - * considered. - * - * Returns: - * A promise which resolves and fails depending on the - * XMPP server response. - */ - members = _.filter(members, function (member) { - return (// We only want those members who have the right - // affiliation (or none, which implies the provided one). - _.isUndefined(member.affiliation) || member.affiliation === affiliation - ); - }); - - var promises = _.map(members, _.bind(this.sendAffiliationIQ, this, affiliation)); - - return Promise.all(promises); - }, - saveAffiliationAndRole: function saveAffiliationAndRole(pres) { - /* Parse the presence stanza for the current user's - * affiliation. - * - * Parameters: - * (XMLElement) pres: A stanza. - */ - var item = sizzle("x[xmlns=\"".concat(Strophe.NS.MUC_USER, "\"] item"), pres).pop(); - var is_self = pres.querySelector("status[code='110']"); - - if (is_self && !_.isNil(item)) { - var affiliation = item.getAttribute('affiliation'); - var role = item.getAttribute('role'); - - if (affiliation) { - this.save({ - 'affiliation': affiliation - }); - } - - if (role) { - this.save({ - 'role': role - }); - } - } - }, - sendAffiliationIQ: function sendAffiliationIQ(affiliation, member) { - var _this2 = this; - - /* Send an IQ stanza specifying an affiliation change. - * - * Paremeters: - * (String) affiliation: affiliation (could also be stored - * on the member object). - * (Object) member: Map containing the member's jid and - * optionally a reason and affiliation. - */ - return new Promise(function (resolve, reject) { - var iq = $iq({ - to: _this2.get('jid'), - type: "set" - }).c("query", { - xmlns: Strophe.NS.MUC_ADMIN - }).c("item", { - 'affiliation': member.affiliation || affiliation, - 'jid': member.jid - }); - - if (!_.isUndefined(member.reason)) { - iq.c("reason", member.reason); - } - - _converse.connection.sendIQ(iq, resolve, reject); - }); - }, - setAffiliations: function setAffiliations(members) { - /* Send IQ stanzas to the server to modify the - * affiliations in this room. - * - * See: http://xmpp.org/extensions/xep-0045.html#modifymember - * - * Parameters: - * (Object) members: A map of jids, affiliations and optionally reasons - * (Function) onSuccess: callback for a succesful response - * (Function) onError: callback for an error response - */ - var affiliations = _.uniq(_.map(members, 'affiliation')); - - _.each(affiliations, _.partial(this.setAffiliation.bind(this), _, members)); - }, - getJidsWithAffiliations: function getJidsWithAffiliations(affiliations) { - var _this3 = this; - - /* Returns a map of JIDs that have the affiliations - * as provided. - */ - if (_.isString(affiliations)) { - affiliations = [affiliations]; - } - - return new Promise(function (resolve, reject) { - var promises = _.map(affiliations, _.partial(_this3.requestMemberList.bind(_this3))); - - Promise.all(promises).then(_.flow(u.marshallAffiliationIQs, resolve), _.flow(u.marshallAffiliationIQs, resolve)); - }); - }, - updateMemberLists: function updateMemberLists(members, affiliations, deltaFunc) { - var _this4 = this; - - /* Fetch the lists of users with the given affiliations. - * Then compute the delta between those users and - * the passed in members, and if it exists, send the delta - * to the XMPP server to update the member list. - * - * Parameters: - * (Object) members: Map of member jids and affiliations. - * (String|Array) affiliation: An array of affiliations or - * a string if only one affiliation. - * (Function) deltaFunc: The function to compute the delta - * between old and new member lists. - * - * Returns: - * A promise which is resolved once the list has been - * updated or once it's been established there's no need - * to update the list. - */ - this.getJidsWithAffiliations(affiliations).then(function (old_members) { - _this4.setAffiliations(deltaFunc(members, old_members)); - }); - }, - checkForReservedNick: function checkForReservedNick(callback, errback) { - /* Use service-discovery to ask the XMPP server whether - * this user has a reserved nickname for this room. - * If so, we'll use that, otherwise we render the nickname form. - * - * Parameters: - * (Function) callback: Callback upon succesful IQ response - * (Function) errback: Callback upon error IQ response - */ - _converse.connection.sendIQ($iq({ - 'to': this.get('jid'), - 'from': _converse.connection.jid, - 'type': "get" - }).c("query", { - 'xmlns': Strophe.NS.DISCO_INFO, - 'node': 'x-roomuser-item' - }), callback, errback); - - return this; - }, isUserMentioned: function isUserMentioned(message) { /* Returns a boolean to indicate whether the current user * was mentioned in a message. @@ -54401,17 +53599,17 @@ Strophe.addConnectionPlugin('disco', } if (u.isNewMessage(stanza) && this.newMessageWillBeHidden()) { - var settings = { + this.save({ 'num_unread_general': this.get('num_unread_general') + 1 - }; + }); if (this.isUserMentioned(body.textContent)) { - settings.num_unread = this.get('num_unread') + 1; + this.save({ + 'num_unread': this.get('num_unread') + 1 + }); _converse.incrementMsgCounter(); } - - this.save(settings); } }, clearUnreadMsgCounter: function clearUnreadMsgCounter() { @@ -54784,6 +53982,8 @@ __p += '">\n'; return __p };}); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -54798,7 +53998,7 @@ return __p */ (function (root, factory) { define('converse-bookmarks',["converse-core", "converse-muc", "tpl!chatroom_bookmark_form", "tpl!chatroom_bookmark_toggle", "tpl!bookmark", "tpl!bookmarks_list"], factory); -})(this, function (converse, muc, tpl_chatroom_bookmark_form, tpl_chatroom_bookmark_toggle, tpl_bookmark, tpl_bookmarks_list) { +})(void 0, function (converse, muc, tpl_chatroom_bookmark_form, tpl_chatroom_bookmark_toggle, tpl_bookmark, tpl_bookmarks_list) { var _converse$env = converse.env, Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, @@ -55169,7 +54369,7 @@ return __p 'jid': bookmark.getAttribute('jid'), 'name': bookmark.getAttribute('name'), 'autojoin': bookmark.getAttribute('autojoin') === 'true', - 'nick': _.get(bookmark.querySelector('nick'), 'textContent') + 'nick': bookmark.querySelector('nick').textContent }); }); }, @@ -55466,6 +54666,8 @@ __e(o.info_title) + return __p };}); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -55480,7 +54682,7 @@ return __p */ (function (root, factory) { define('converse-roomslist',["utils", "converse-core", "converse-muc", "tpl!rooms_list", "tpl!rooms_list_item"], factory); -})(this, function (utils, converse, muc, tpl_rooms_list, tpl_rooms_list_item) { +})(void 0, function (utils, converse, muc, tpl_rooms_list, tpl_rooms_list_item) { var _converse$env = converse.env, Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, @@ -55832,6 +55034,8 @@ Strophe.RSM.prototype = { }; })); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -55843,7 +55047,7 @@ Strophe.RSM.prototype = { // XEP-0059 Result Set Management (function (root, factory) { define('converse-mam',["sizzle", "converse-core", "utils", "converse-disco", "strophe.rsm"], factory); -})(this, function (sizzle, converse, utils) { +})(void 0, function (sizzle, converse, utils) { "use strict"; var CHATROOMS_TYPE = 'chatroom'; @@ -56390,7 +55594,7 @@ return __p define('tpl!chatarea', ['lodash'], function(_) {return function(o) { var __t, __p = '', __e = _.escape, __j = Array.prototype.join; function print() { __p += __j.call(arguments, '') } -__p += '
    \n
    \n
    \n \n \n
  • \n'; } ; -__p += '\n'; - if (o.show_fileUpload_button) { ; -__p += '\n\n
  • \n \n
  • \n'; - } ; __p += '\n'; if (o.show_call_button) { ; __p += '\n
  • = 0) { + return item.affiliation !== old_list[idx].affiliation; + } + + return false; + })); + } + + if (remove_absentees) { + // Get the removed affiliations + delta = delta.concat(_.map(_.difference(old_jids, new_jids), function (jid) { + return { + 'jid': jid, + 'affiliation': 'none' + }; + })); + } + + return delta; + }, + sendAffiliationIQ: function sendAffiliationIQ(chatroom_jid, affiliation, member) { + /* Send an IQ stanza specifying an affiliation change. + * + * Paremeters: + * (String) chatroom_jid: JID of the relevant room + * (String) affiliation: affiliation (could also be stored + * on the member object). + * (Object) member: Map containing the member's jid and + * optionally a reason and affiliation. + */ + return new Promise(function (resolve, reject) { + var iq = $iq({ + to: chatroom_jid, + type: "set" + }).c("query", { + xmlns: Strophe.NS.MUC_ADMIN + }).c("item", { + 'affiliation': member.affiliation || affiliation, + 'jid': member.jid + }); + + if (!_.isUndefined(member.reason)) { + iq.c("reason", member.reason); + } + + _converse.connection.sendIQ(iq, resolve, reject); + }); + }, + setAffiliation: function setAffiliation(affiliation, members) { + /* Send IQ stanzas to the server to set an affiliation for + * the provided JIDs. + * + * See: http://xmpp.org/extensions/xep-0045.html#modifymember + * + * XXX: Prosody doesn't accept multiple JIDs' affiliations + * being set in one IQ stanza, so as a workaround we send + * a separate stanza for each JID. + * Related ticket: https://prosody.im/issues/issue/795 + * + * Parameters: + * (String) affiliation: The affiliation + * (Object) members: A map of jids, affiliations and + * optionally reasons. Only those entries with the + * same affiliation as being currently set will be + * considered. + * + * Returns: + * A promise which resolves and fails depending on the + * XMPP server response. + */ + members = _.filter(members, function (member) { + return (// We only want those members who have the right + // affiliation (or none, which implies the provided + // one). + _.isUndefined(member.affiliation) || member.affiliation === affiliation + ); + }); + + var promises = _.map(members, _.partial(this.sendAffiliationIQ, this.model.get('jid'), affiliation)); + + return Promise.all(promises); + }, + setAffiliations: function setAffiliations(members) { + /* Send IQ stanzas to the server to modify the + * affiliations in this room. + * + * See: http://xmpp.org/extensions/xep-0045.html#modifymember + * + * Parameters: + * (Object) members: A map of jids, affiliations and optionally reasons + * (Function) onSuccess: callback for a succesful response + * (Function) onError: callback for an error response + */ + var affiliations = _.uniq(_.map(members, 'affiliation')); + + _.each(affiliations, _.partial(this.setAffiliation.bind(this), _, members)); + }, + marshallAffiliationIQs: function marshallAffiliationIQs() { + /* Marshall a list of IQ stanzas into a map of JIDs and + * affiliations. + * + * Parameters: + * Any amount of XMLElement objects, representing the IQ + * stanzas. + */ + return _.flatMap(arguments[0], this.parseMemberListIQ); + }, + getJidsWithAffiliations: function getJidsWithAffiliations(affiliations) { + var _this4 = this; + + /* Returns a map of JIDs that have the affiliations + * as provided. + */ + if (_.isString(affiliations)) { + affiliations = [affiliations]; + } + + return new Promise(function (resolve, reject) { + var promises = _.map(affiliations, _.partial(_this4.requestMemberList, _this4.model.get('jid'))); + + Promise.all(promises).then(_.flow(_this4.marshallAffiliationIQs.bind(_this4), resolve), _.flow(_this4.marshallAffiliationIQs.bind(_this4), resolve)); + }); + }, + updateMemberLists: function updateMemberLists(members, affiliations, deltaFunc) { + var _this5 = this; + + /* Fetch the lists of users with the given affiliations. + * Then compute the delta between those users and + * the passed in members, and if it exists, send the delta + * to the XMPP server to update the member list. + * + * Parameters: + * (Object) members: Map of member jids and affiliations. + * (String|Array) affiliation: An array of affiliations or + * a string if only one affiliation. + * (Function) deltaFunc: The function to compute the delta + * between old and new member lists. + * + * Returns: + * A promise which is resolved once the list has been + * updated or once it's been established there's no need + * to update the list. + */ + this.getJidsWithAffiliations(affiliations).then(function (old_members) { + _this5.setAffiliations(deltaFunc(members, old_members)); + }); + }, + directInvite: function directInvite(recipient, reason) { + /* Send a direct invitation as per XEP-0249 + * + * Parameters: + * (String) recipient - JID of the person being invited + * (String) reason - Optional reason for the invitation + */ + if (this.model.get('membersonly')) { + // When inviting to a members-only room, we first add + // the person to the member list by giving them an + // affiliation of 'member' (if they're not affiliated + // already), otherwise they won't be able to join. + var map = {}; + map[recipient] = 'member'; + + var deltaFunc = _.partial(this.computeAffiliationsDelta, true, false); + + this.updateMemberLists([{ + 'jid': recipient, + 'affiliation': 'member', + 'reason': reason + }], ['member', 'owner', 'admin'], deltaFunc); + } + + var attrs = { + 'xmlns': 'jabber:x:conference', + 'jid': this.model.get('jid') + }; + + if (reason !== null) { + attrs.reason = reason; + } + + if (this.model.get('password')) { + attrs.password = this.model.get('password'); + } + + var invitation = $msg({ + from: _converse.connection.jid, + to: recipient, + id: _converse.connection.getUniqueId() + }).c('x', attrs); + + _converse.connection.send(invitation); + + _converse.emit('roomInviteSent', { + 'room': this, + 'recipient': recipient, + 'reason': reason + }); + }, handleChatStateMessage: function handleChatStateMessage(message) { /* Override the method on the ChatBoxView base class to * ignore notifications in groupchats. @@ -57496,7 +56973,7 @@ return __p * Parameters: * (String) text: The message text to be sent. */ - text = u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse); + text = emojione.shortnameToUnicode(text); var msgid = _converse.connection.getUniqueId(); @@ -57519,20 +56996,6 @@ return __p msgid: msgid }); }, - sendChatRoomFile: function sendChatRoomFile(text) { - var msgid = _converse.connection.getUniqueId(); - - var stanza = $msg({ - 'from': _converse.connection.jid, - 'to': this.model.get('jid'), - 'type': 'groupchat', - 'id': msgid - }).c("body").t(text).up().c("x", { - 'xmlns': Strophe.NS.OUTOFBAND - }).c('url').t(text).up(); - - _converse.connection.send(stanza); - }, modifyRole: function modifyRole(room, nick, role, reason, onSuccess, onError) { var item = $build("item", { nick: nick, @@ -57581,19 +57044,13 @@ return __p onCommandError: function onCommandError() { this.showStatusNotification(__("Error: could not execute the command"), true); }, - onMessageSubmitted: function onMessageSubmitted(text, notNeeded) { - var file = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; - + onMessageSubmitted: function onMessageSubmitted(text) { /* Gets called when the user presses enter to send off a * message in a chat room. * * Parameters: * (String) text - The message text. */ - if (file !== null) { - return this.sendChatRoomFile(text); - } - if (_converse.muc_disable_moderator_commands) { return this.sendChatRoomMessage(text); } @@ -57608,7 +57065,7 @@ return __p break; } - this.model.setAffiliation('admin', [{ + this.setAffiliation('admin', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57619,7 +57076,7 @@ return __p break; } - this.model.setAffiliation('outcast', [{ + this.setAffiliation('outcast', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57662,7 +57119,7 @@ return __p break; } - this.model.setAffiliation('member', [{ + this.setAffiliation('member', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57682,7 +57139,7 @@ return __p break; } - this.model.setAffiliation('owner', [{ + this.setAffiliation('owner', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57701,7 +57158,7 @@ return __p break; } - this.model.setAffiliation('none', [{ + this.setAffiliation('none', [{ 'jid': args[0], 'reason': args[1] }]).then(null, this.onCommandError.bind(this)); @@ -57817,8 +57274,7 @@ return __p nick = nick ? nick : this.model.get('nick'); if (!nick) { - this.checkForReservedNick(); - return this; + return this.checkForReservedNick(); } if (this.model.get('connection_status') === converse.ROOMSTATUS.ENTERED) { @@ -57888,7 +57344,7 @@ return __p _converse.ChatBoxView.prototype.close.apply(this, arguments); }, renderConfigurationForm: function renderConfigurationForm(stanza) { - var _this4 = this; + var _this6 = this; /* Renders a form given an IQ stanza containing the current * room configuration. @@ -57933,16 +57389,48 @@ return __p last_fieldset_el.querySelector('input[type=button]').addEventListener('click', function (ev) { ev.preventDefault(); - _this4.closeForm(); + _this6.closeForm(); }); form_el.addEventListener('submit', function (ev) { ev.preventDefault(); - _this4.saveConfiguration(ev.target).then(_this4.getRoomFeatures.bind(_this4)); + _this6.saveConfiguration(ev.target).then(_this6.getRoomFeatures.bind(_this6)); }, false); }, + sendConfiguration: function sendConfiguration(config, onSuccess, onError) { + /* Send an IQ stanza with the room configuration. + * + * Parameters: + * (Array) config: The room configuration + * (Function) onSuccess: Callback upon succesful IQ response + * The first parameter passed in is IQ containing the + * room configuration. + * The second is the response IQ from the server. + * (Function) onError: Callback upon error IQ response + * The first parameter passed in is IQ containing the + * room configuration. + * The second is the response IQ from the server. + */ + var iq = $iq({ + to: this.model.get('jid'), + type: "set" + }).c("query", { + xmlns: Strophe.NS.MUC_OWNER + }).c("x", { + xmlns: Strophe.NS.XFORM, + type: "submit" + }); + + _.each(config || [], function (node) { + iq.cnode(node).up(); + }); + + onSuccess = _.isUndefined(onSuccess) ? _.noop : _.partial(onSuccess, iq.nodeTree); + onError = _.isUndefined(onError) ? _.noop : _.partial(onError, iq.nodeTree); + return _converse.connection.sendIQ(iq, onSuccess, onError); + }, saveConfiguration: function saveConfiguration(form) { - var _this5 = this; + var _this7 = this; /* Submit the room configuration form by sending an IQ * stanza to the server. @@ -57957,13 +57445,13 @@ return __p var inputs = form ? sizzle(':input:not([type=button]):not([type=submit])', form) : [], configArray = _.map(inputs, u.webForm2xForm); - _this5.model.sendConfiguration(configArray, resolve, reject); + _this7.sendConfiguration(configArray, resolve, reject); - _this5.closeForm(); + _this7.closeForm(); }); }, autoConfigureChatRoom: function autoConfigureChatRoom() { - var _this6 = this; + var _this8 = this; /* Automatically configure room based on the * 'roomconfig' data on this view's model. @@ -57977,7 +57465,7 @@ return __p */ var that = this; return new Promise(function (resolve, reject) { - _this6.fetchRoomConfiguration().then(function (stanza) { + _this8.fetchRoomConfiguration().then(function (stanza) { var configArray = [], fields = stanza.querySelectorAll('field'), config = that.model.get('roomconfig'); @@ -58009,7 +57497,7 @@ return __p configArray.push(field); if (! --count) { - that.model.sendConfiguration(configArray, resolve, reject); + that.sendConfiguration(configArray, resolve, reject); } }); }); @@ -58023,7 +57511,7 @@ return __p this.renderAfterTransition(); }, fetchRoomConfiguration: function fetchRoomConfiguration(handler) { - var _this7 = this, + var _this9 = this, _arguments = arguments; /* Send an IQ stanza to fetch the room configuration data. @@ -58035,13 +57523,13 @@ return __p */ return new Promise(function (resolve, reject) { _converse.connection.sendIQ($iq({ - 'to': _this7.model.get('jid'), + 'to': _this9.model.get('jid'), 'type': "get" }).c("query", { xmlns: Strophe.NS.MUC_OWNER }), function (iq) { if (handler) { - handler.apply(_this7, _arguments); + handler.apply(_this9, _arguments); } resolve(iq); @@ -58049,14 +57537,57 @@ return __p ); }); }, + parseRoomFeatures: function parseRoomFeatures(iq) { + /* See http://xmpp.org/extensions/xep-0045.html#disco-roominfo + * + * + * + * + * + * + * + * + * + * + */ + var features = { + 'features_fetched': true, + 'name': iq.querySelector('identity').getAttribute('name') + }; + + _.each(iq.querySelectorAll('feature'), function (field) { + var fieldname = field.getAttribute('var'); + + if (!fieldname.startsWith('muc_')) { + if (fieldname === Strophe.NS.MAM) { + features.mam_enabled = true; + } + + return; + } + + features[fieldname.replace('muc_', '')] = true; + }); + + var desc_field = iq.querySelector('field[var="muc#roominfo_description"] value'); + + if (!_.isNull(desc_field)) { + features.description = desc_field.textContent; + } + + this.model.save(features); + }, getRoomFeatures: function getRoomFeatures() { - var _this8 = this; + var _this10 = this; /* Fetch the room disco info, parse it and then * save it on the Backbone.Model of this chat rooms. */ return new Promise(function (resolve, reject) { - _converse.connection.disco.info(_this8.model.get('jid'), null, _.flow(_this8.model.parseRoomFeatures.bind(_this8.model), resolve), function () { + _converse.connection.disco.info(_this10.model.get('jid'), null, _.flow(_this10.parseRoomFeatures.bind(_this10), resolve), function () { reject(new Error("Could not parse the room features")); }, 5000); }); @@ -58100,10 +57631,20 @@ return __p checkForReservedNick: function checkForReservedNick() { /* User service-discovery to ask the XMPP server whether * this user has a reserved nickname for this room. - * If so, we'll use that, otherwise we render the nickname form. + * If so, we'll use that, otherwise we render the nickname + * form. */ this.showSpinner(); - this.model.checkForReservedNick(this.onNickNameFound.bind(this), this.onNickNameNotFound.bind(this)); + + _converse.connection.sendIQ($iq({ + 'to': this.model.get('jid'), + 'from': _converse.connection.jid, + 'type': "get" + }).c("query", { + 'xmlns': Strophe.NS.DISCO_INFO, + 'node': 'x-roomuser-item' + }), this.onNickNameFound.bind(this), this.onNickNameNotFound.bind(this)); + return this; }, onNickNameFound: function onNickNameFound(iq) { @@ -58264,6 +57805,33 @@ return __p return; }, + saveAffiliationAndRole: function saveAffiliationAndRole(pres) { + /* Parse the presence stanza for the current user's + * affiliation. + * + * Parameters: + * (XMLElement) pres: A stanza. + */ + var item = sizzle("x[xmlns=\"".concat(Strophe.NS.MUC_USER, "\"] item"), pres).pop(); + var is_self = pres.querySelector("status[code='110']"); + + if (is_self && !_.isNil(item)) { + var affiliation = item.getAttribute('affiliation'); + var role = item.getAttribute('role'); + + if (affiliation) { + this.model.save({ + 'affiliation': affiliation + }); + } + + if (role) { + this.model.save({ + 'role': role + }); + } + } + }, parseXUserElement: function parseXUserElement(x, stanza, is_self) { /* Parse the passed-in * element and construct a map containing relevant @@ -58317,7 +57885,7 @@ return __p return notification; }, displayNotificationsforUser: function displayNotificationsforUser(notification) { - var _this9 = this; + var _this11 = this; /* Given the notification object generated by * parseXUserElement, display any relevant messages and @@ -58339,7 +57907,7 @@ return __p } _.each(notification.messages, function (message) { - _this9.content.insertAdjacentHTML('beforeend', tpl_info({ + _this11.content.insertAdjacentHTML('beforeend', tpl_info({ 'data': '', 'isodate': moment().format(), 'extra_classes': 'chat-event', @@ -58565,7 +58133,7 @@ return __p * Parameters: * (XMLElement) pres: The stanza */ - this.model.saveAffiliationAndRole(pres); + this.saveAffiliationAndRole(pres); var locked_room = pres.querySelector("status[code='201']"); if (locked_room) { @@ -59038,7 +58606,7 @@ return __p var reason = prompt(__('You are about to invite %1$s to the chat room "%2$s". ' + 'You may optionally include a message, explaining the reason for the invitation.', suggestion.text.label, this.model.get('id'))); if (reason !== null) { - this.chatroomview.model.directInvite(suggestion.text.value, reason); + this.chatroomview.directInvite(suggestion.text.value, reason); } var form = suggestion.target.form, @@ -59164,6 +58732,8 @@ return __p }); }); //# sourceMappingURL=converse-muc-views.js.map; + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -59172,7 +58742,7 @@ return __p // (function (root, factory) { define('converse-muc-embedded',["converse-core", "converse-muc"], factory); -})(this, function (converse) { +})(void 0, function (converse) { "use strict"; var _converse$env = converse.env, @@ -66523,6 +66093,8 @@ CryptoJS.mode.CTR = (function () { } })); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -66537,7 +66109,7 @@ CryptoJS.mode.CTR = (function () { */ (function (root, factory) { define('converse-otr',["converse-chatview", "bootstrap", "tpl!toolbar_otr", 'otr'], factory); -})(this, function (converse, bootstrap, tpl_toolbar_otr, otr) { +})(void 0, function (converse, bootstrap, tpl_toolbar_otr, otr) { "use strict"; var _converse$env = converse.env, @@ -67133,6 +66705,8 @@ __p += '\n'; return __p };}); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -67147,7 +66721,7 @@ return __p */ (function (root, factory) { define('converse-register',["form-utils", "converse-core", "tpl!form_username", "tpl!register_link", "tpl!register_panel", "tpl!registration_form", "tpl!registration_request", "tpl!form_input", "tpl!spinner", "converse-controlbox"], factory); -})(this, function (utils, converse, tpl_form_username, tpl_register_link, tpl_register_panel, tpl_registration_form, tpl_registration_request, tpl_form_input, tpl_spinner) { +})(void 0, function (utils, converse, tpl_form_username, tpl_register_link, tpl_register_panel, tpl_registration_form, tpl_registration_request, tpl_form_input, tpl_spinner) { "use strict"; // Strophe methods for building stanzas var _converse$env = converse.env, @@ -67935,6 +67509,8 @@ return __p }); })); + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -67949,7 +67525,7 @@ return __p */ (function (root, factory) { define('converse-ping',["converse-core", "strophe.ping"], factory); -})(this, function (converse) { +})(void 0, function (converse) { "use strict"; // Strophe methods for building stanzas var _converse$env = converse.env, @@ -68057,6 +67633,8 @@ return __p }); }); //# sourceMappingURL=converse-ping.js.map; + + // Converse.js (A browser based XMPP chat client) // http://conversejs.org // @@ -68067,7 +67645,7 @@ return __p /*global define */ (function (root, factory) { define('converse-notification',["converse-core"], factory); -})(this, function (converse) { +})(void 0, function (converse) { "use strict"; var _converse$env = converse.env, @@ -68414,6 +67992,8 @@ __p += '\n Date: Wed, 11 Apr 2018 07:49:46 +0200 Subject: [PATCH 07/12] added tests from http-file-upload-Branch --- spec/headline.js | 2 +- spec/http-file-upload.js | 205 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 spec/http-file-upload.js diff --git a/spec/headline.js b/spec/headline.js index 50ab67167..d072b0d44 100644 --- a/spec/headline.js +++ b/spec/headline.js @@ -42,7 +42,7 @@ it("will open and display headline messages", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { - + /* 0; + }, 300).then(function () { + /* + * + * + * + * + * + * + */ + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + })[0]; + var info_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + + stanza = $iq({ + 'type': 'result', + 'from': 'localhost', + 'to': 'dummy@localhost/resource', + 'id': info_IQ_id + }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'}) + .c('identity', { + 'category': 'server', + 'type': 'im'}).up() + .c('feature', { + 'var': 'http://jabber.org/protocol/disco#info'}).up() + .c('feature', { + 'var': 'http://jabber.org/protocol/disco#items'}); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + _converse.api.disco.entities.get().then(function(entities) { + expect(entities.length).toBe(2); + expect(_.includes(entities.pluck('jid'), 'localhost')).toBe(true); + expect(_.includes(entities.pluck('jid'), 'dummy@localhost')).toBe(true); + + expect(entities.get(_converse.domain).features.length).toBe(2); + expect(entities.get(_converse.domain).identities.length).toBe(1); + + return test_utils.waitUntil(function () { + // Converse.js sees that the entity has a disco#items feature, + // so it will make a query for it. + return _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]'); + }).length > 0; + }, 300); + }); + }).then(function () { + /* + * + * + * + * + * + */ + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]'); + })[0]; + var items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + stanza = $iq({ + 'type': 'result', + 'from': 'localhost', + 'to': 'dummy@localhost/resource', + 'id': items_IQ_id + }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#items'}) + .c('item', { + 'jid': 'upload.localhost', + 'name': 'HTTP File Upload'}); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + _converse.api.disco.entities.get().then(function (entities) { + expect(entities.length).toBe(2); + expect(entities.get('localhost').items.length).toBe(1); + return test_utils.waitUntil(function () { + // Converse.js sees that the entity has a disco#info feature, + // so it will make a query for it. + return _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="upload.localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + }).length > 0; + }, 300); + }); + }).then(function () { + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="upload.localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + })[0]; + var IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + expect(stanza.toLocaleString()).toBe( + ""+ + ""+ + ""); + + // Upload service responds and reports a maximum file size of 5MiB + /* + * + * + * + * + * + * urn:xmpp:http:upload:0 + * + * + * 5242880 + * + * + * + * + */ + stanza = $iq({'type': 'result', 'to': 'dummy@localhost/resource', 'id': IQ_id, 'from': 'upload.localhost'}) + .c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'}) + .c('identity', {'category':'store', 'type':'file', 'name':'HTTP File Upload'}).up() + .c('feature', {'var':'urn:xmpp:http:upload:0'}).up() + .c('x', {'type':'result', 'xmlns':'jabber:x:data'}) + .c('field', {'var':'FORM_TYPE', 'type':'hidden'}) + .c('value').t('urn:xmpp:http:upload:0').up().up() + .c('field', {'var':'max-file-size'}) + .c('value').t('5242880'); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + _converse.api.disco.entities.get().then(function (entities) { + expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1); + _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then( + function (result) { + expect(result.length).toBe(1); + expect(result[0].get('jid')).toBe('upload.localhost'); + done(); + } + ); + }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); + }) + }) + })); + }); + + describe("When supported", function () { + + describe("A file upload toolbar button", function () { + + it("appears in private chats", mock.initConverseWithAsync(function (done, _converse) { + test_utils.waitUntilDiscoConfirmed( + _converse, _converse.domain, + [{'category': 'server', 'type':'IM'}], + ['http://jabber.org/protocol/disco#items'], [], 'info').then(function () { + + test_utils.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.localhost'], 'items').then(function () { + test_utils.waitUntilDiscoConfirmed(_converse, 'upload.localhost', [], [Strophe.NS.HTTPUPLOAD], []).then(function () { + test_utils.createContacts(_converse, 'current'); + var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; + test_utils.openChatBoxFor(_converse, contact_jid); + var view = _converse.chatboxviews.get(contact_jid); + test_utils.waitUntil(function () { + return view.el.querySelector('.upload-file'); + }, 150).then(function () { + expect(view.el.querySelector('.chat-toolbar .upload-file')).not.toBe(null); + done(); + }); + }); + }); + }); + })); + + it("appears in MUC chats", mock.initConverseWithAsync(function (done, _converse) { + done(); + })); + }); + }); + }); +})); From fa8074965814d371ad38fa5eaf005301e2f79092 Mon Sep 17 00:00:00 2001 From: worlword Date: Wed, 11 Apr 2018 15:29:41 +0200 Subject: [PATCH 08/12] refactoring based on the review from jcbrand 06.Apr.2018 --- spec/http-file-upload.js | 1 + src/build.js | 2 +- src/config.js | 2 +- src/converse-chatboxes.js | 121 +++++++++++++- src/converse-chatview.js | 36 ++--- src/converse-core.js | 2 +- src/converse-http-file-upload.js | 35 ++++ src/converse-httpFileUpload.js | 269 ------------------------------- src/converse-muc-views.js | 15 +- src/converse-muc.js | 12 ++ src/converse.js | 2 +- src/inverse.js | 2 +- tests/runner-transpiled.js | 2 +- 13 files changed, 182 insertions(+), 319 deletions(-) create mode 100644 src/converse-http-file-upload.js delete mode 100644 src/converse-httpFileUpload.js diff --git a/spec/http-file-upload.js b/spec/http-file-upload.js index e17866c18..3541f8f4f 100644 --- a/spec/http-file-upload.js +++ b/spec/http-file-upload.js @@ -158,6 +158,7 @@ expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1); _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then( function (result) { + console.log("in the test the service is supported"); expect(result.length).toBe(1); expect(result[0].get('jid')).toBe('upload.localhost'); done(); diff --git a/src/build.js b/src/build.js index a2a1f3b4f..8aab2a903 100644 --- a/src/build.js +++ b/src/build.js @@ -13,7 +13,7 @@ "converse-dragresize": "builds/converse-dragresize", "converse-fullscreen": "builds/converse-fullscreen", "converse-headline": "builds/converse-headline", - "converse-httpFileUpload": "builds/converse-httpFileUpload", + "converse-http-file-upload":"builds/converse-http-file-upload", "converse-mam": "builds/converse-mam", "converse-minimize": "builds/converse-minimize", "converse-modal": "builds/converse-modal", diff --git a/src/config.js b/src/config.js index 96758d97e..18f4073fa 100644 --- a/src/config.js +++ b/src/config.js @@ -74,7 +74,7 @@ require.config({ "converse-dragresize": "src/converse-dragresize", "converse-fullscreen": "src/converse-fullscreen", "converse-headline": "src/converse-headline", - "converse-httpFileUpload": "src/converse-httpFileUpload", + "converse-http-file-upload":"src/converse-http-file-upload", "converse-mam": "src/converse-mam", "converse-minimize": "src/converse-minimize", "converse-modal": "src/converse-modal", diff --git a/src/converse-chatboxes.js b/src/converse-chatboxes.js index 3a0457a0f..9eff30d6a 100644 --- a/src/converse-chatboxes.js +++ b/src/converse-chatboxes.js @@ -15,8 +15,8 @@ }(this, function (converse, tpl_chatboxes) { "use strict"; - const { Backbone, Promise, Strophe, b64_sha1, moment, utils, _ } = converse.env; - + const { $msg, Backbone, Promise, Strophe, b64_sha1, moment, utils, _ } = converse.env; + Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob'); converse.plugins.add('converse-chatboxes', { @@ -50,7 +50,8 @@ /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ - const { _converse } = this; + const { _converse } = this, + { __ } = _converse; _converse.api.promises.add([ 'chatBoxesFetched', @@ -115,6 +116,120 @@ }); }, + createFileMessageStanza(message, to){ + const stanza = $msg({ + 'from': _converse.connection.jid, + 'to': to, + 'type': 'chat', + 'id': message.get('msgid') + }).c('body').t(message.get('message')).up() + .c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up() + .c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('message')).up(); + + return stanza; + }, + + sendFile (file, chatbox) { + const self = this; + console.log('Send file via http upload'); + const request_slot_url = 'upload.' + _converse.domain; + _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, request_slot_url) + .then((result) => { + chatbox.showHelpMessages([__('The file upload starts now')],'info'); + self.requestSlot(file, request_slot_url, function(data) { + if (!data) { + // general error + console.log('Unknown error while requesting upload slot.'); + alert(__('File upload failed. Please check the log.')); + } else if (data.error) { + // specific error + console.log('The XMPP-Server return an error of the type: ' + data.error.type); + alert(__('File upload failed. Please check the log.')); + } else if (data.get && data.put) { + console.log('slot received, start upload to ' + data.put); + self.uploadFile(data.put, file, function() { + console.log(data.put); + chatbox.onMessageSubmitted(data.put, null, file); + }); + } + }); + }); + }, + + requestSlot (file, request_slot_url, cb) { + const self = this; + console.log("try sending file to: " + request_slot_url); + const iq = converse.env.$iq({ + to: request_slot_url, + type: 'get' + }).c('request', { + xmlns: Strophe.NS.HTTPUPLOAD + }).c('filename').t(file.name) + .up() + .c('size').t(file.size); + + _converse.connection.sendIQ(iq, function(stanza) { + self.successfulRequestSlotCB(stanza, cb); + }, function(stanza) { + self.failedRequestSlotCB(stanza, cb); + }); + }, + + /** + * Upload the given file to the given url. + */ + uploadFile (url, file, success_cb) { + console.log("uploadFile start"); + const xmlhttp = new XMLHttpRequest(); + const contentType = 'application/octet-stream'; + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState === XMLHttpRequest.DONE) { + console.log("Status: " + xmlhttp.status); + if (xmlhttp.status === 200 || xmlhttp.status === 201) { + console.log('file successful uploaded'); + if (success_cb) { + success_cb(); + } + } + else { + console.log('error while uploading file to ' + url); + alert(__('Could not upload File please try again.')); + } + } + }; + + xmlhttp.open('PUT', url, true); + xmlhttp.setRequestHeader("Content-type", contentType); + xmlhttp.send(file); + + console.log("uploadFile end"); + }, + + /** + * Process successful response to slot request. + */ + successfulRequestSlotCB (stanza, cb) { + const slot = stanza.getElementsByTagName('slot')[0]; + + if (slot != undefined) { + var put = slot.getElementsByTagName('put')[0].textContent; + var get = slot.getElementsByTagName('get')[0].textContent; + cb({ + put: put, + get: get + }); + } else { + this.failedRequestSlotCB(stanza, cb); + } + }, + + /** + * Process failed response to slot request. + */ + failedRequestSlotCB (stanza, cb) { + alert(__('Could not upload File please try again.')); + }, + getMessageBody (message) { const type = message.getAttribute('type'); return (type === 'error') ? diff --git a/src/converse-chatview.js b/src/converse-chatview.js index 344e9b54f..7b8b79746 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -25,7 +25,7 @@ "tpl!spoiler_button", "tpl!spoiler_message", "tpl!toolbar", - "converse-httpFileUpload", + "converse-http-file-upload", "converse-chatboxes" ], factory); }(this, function ( @@ -56,8 +56,6 @@ FORWARD_SLASH: 47 }; - Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob'); - converse.plugins.add('converse-chatview', { /* Plugin dependencies are other plugins which might be * overridden or relied upon, and therefore need to be loaded before @@ -236,7 +234,6 @@ } }); - _converse.ChatBoxView = Backbone.NativeView.extend({ length: 200, className: 'chatbox hidden', @@ -257,17 +254,14 @@ 'change .fileUpload_input': 'handleFileSelect' }, - toggleFileUpload(ev) { - _converse.FileUpload.prototype.initFiletransfer(_converse.connection); - var uploadDialog = this.el.querySelector('.fileUpload_input'); - uploadDialog.click(); + toggleFileUpload (ev) { + this.el.querySelector('.fileUpload_input').click(); }, - handleFileSelect(evt) { + handleFileSelect (evt) { var files = evt.target.files; var file = files[0]; - var jid = this.jid; - _converse.FileUpload.prototype.setFile(file,this); + this.model.sendFile(file, this); }, initialize () { @@ -662,9 +656,9 @@ this.renderSpoilerMessage(msg, attrs) } - if(msg_content.textContent.endsWith('mp4')){ + if (msg_content.textContent.endsWith('mp4')) { msg_content.innerHTML = u.renderMovieURLs(msg_content); - } else if(msg_content.textContent.endsWith('mp3')){ + } else if (msg_content.textContent.endsWith('mp3')) { msg_content.innerHTML = u.renderAudioURLs(msg_content); } else { u.renderImageURLs(msg_content).then(this.scrollDown.bind(this)); @@ -800,18 +794,7 @@ return stanza; }, - createFileMessageStanza(message){ - const stanza = $msg({ - 'from': _converse.connection.jid, - 'to': this.model.get('jid'), - 'type': 'chat', - 'id': message.get('msgid') - }).c('body').t(message.get('message')).up() - .c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up() - .c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('message')).up(); - return stanza; - }, sendMessage (message, file=null) { /* Responsible for sending off a text message. @@ -823,7 +806,7 @@ // Especially in the OTR case. var messageStanza; if(file !== null){ - messageStanza = this.createFileMessageStanza(message); + messageStanza = this.createFileMessageStanza(message, this.model.get('jid')); } else { messageStanza = this.createMessageStanza(message); @@ -883,8 +866,7 @@ } const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint); const message = this.model.messages.create(attrs); - - /* check, if a file was send. If true it will send the file with XEP-0066. */ + this.sendMessage(message, file); }, diff --git a/src/converse-core.js b/src/converse-core.js index a3a95a358..2e827918f 100644 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -78,7 +78,7 @@ 'converse-dropdown', 'converse-fullscreen', 'converse-headline', - 'converse-httpFileUpload', + 'converse-http-file-upload', 'converse-mam', 'converse-minimize', 'converse-modal', diff --git a/src/converse-http-file-upload.js b/src/converse-http-file-upload.js new file mode 100644 index 000000000..34fdf1272 --- /dev/null +++ b/src/converse-http-file-upload.js @@ -0,0 +1,35 @@ +/** + * Adds Support for Http File Upload (XEP-0363) + * + * @see {@link http://xmpp.org/extensions/xep-0363.html} + */ +(function (root, factory) { + define([ + "converse-core", + ], factory); +}(this, function ( + converse + ) { + "use strict"; + const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env; + Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload'); + + converse.plugins.add('converse-http-file-upload', { + + dependencies: ["converse-chatboxes", "converse-chatview"], + + initialize () { + const { _converse } = this, + { __ } = _converse; + + _converse.FileUpload = Backbone.NativeView.extend({ + /** + * Request upload slot from xmpp-server + */ + + }) + } + }); + + return converse; +})); diff --git a/src/converse-httpFileUpload.js b/src/converse-httpFileUpload.js deleted file mode 100644 index 0ea3ed982..000000000 --- a/src/converse-httpFileUpload.js +++ /dev/null @@ -1,269 +0,0 @@ -/* - The MIT License (MIT) - - Copyright (c) 2014 Klaus Herberth - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/** - * Implements Http File Upload (XEP-0363) - * - * @see {@link http://xmpp.org/extensions/xep-0363.html} - */ -(function (root, factory) { - define([ - "converse-core", - ], factory); -}(this, function ( - converse - ) { - "use strict"; - const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env; - - Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload'); - - var requestSlotUrl; - var ready; - var httpUploadOption = { - enable: true - } - - converse.plugins.add('converse-httpFileUpload', { - - dependencies: ["converse-chatboxes", "converse-disco"], - - initialize() { - const { _converse } = this, - { __ } = _converse; - var connection = _converse.connection; - var domain; - var file; - var chatBox; - - _converse.FileUpload = Backbone.NativeView.extend({ - /** - * Set up http file upload. - * - * @param {*} connection the current strophe-connection - */ - initFiletransfer () { - connection = _converse.connection; - domain = _converse.connection.domain; - - if (httpUploadOption && requestSlotUrl != undefined) { - ready = true; - return; - } - this.discoverUploadService(); - }, - - /** - * Discover upload service for http upload. - * - */ - discoverUploadService () { - var self = this; - console.log('discover http upload service'); - connection.disco.items(domain, null, function(items) { - var childs = items.getElementsByTagName('item'); - for(var i = 0; i < childs.length; i++){ - var jid = childs[i].attributes.jid.value; - if (ready) { - // abort, because we already found a service - return false; - } - self.queryItemForUploadService(jid); - } - }); - }, - - /** - * Query item for upload service. - * - * @param {String} jid of the logged-in user - * @param {Function} cb Callback on success - */ - queryItemForUploadService (jid) { - var self = this; - console.log('query ' + jid + ' for upload service'); - - connection.disco.info(jid, null, function(info) { - var httpUploadFeature; - var temp = info.getElementsByTagName('feature'); - for(var i = 0; i < temp.length; i++){ - var feature = temp[i].attributes.var; - if(feature != undefined && feature.value === Strophe.NS.HTTPUPLOAD){ - requestSlotUrl = jid; - ready = true; - self.sendFile(); - } - } - }); - }, - - /** - * Saves the file the user has picked. - * - * @param {*} file the name of the file the user has picked. - * @param {*} chatBox the chatbox from which the user initiated the file-upload - */ - setFile (file1, chatBox1){ - file = file1; - chatBox = chatBox1; - this.sendFile(); - }, - - /** - * Upload file. - * Waits till the Upload-Service is discovered and till the user has picked a file. - * - */ - sendFile () { - var self = this; - if(file === undefined){ - console.log("waiting to choose a file"); - return; - } - else if(requestSlotUrl === undefined){ - console.log("waiting for service discovery"); - return; - } - - console.log('Send file via http upload'); - chatBox.showHelpMessages([__('The file upload starts now')],'info'); - this.requestSlot(file, function(data) { - if (!data) { - // general error - console.log('Unknown error while requesting upload slot.'); - alert(__('File upload failed. Please check the log.')); - } else if (data.error) { - // specific error - console.log('The XMPP-Server return an error of the type: ' + data.error.type); - alert(__('File upload failed. Please check the log.')); - } else if (data.get && data.put) { - console.log('slot received, start upload to ' + data.put); - self.uploadFile(data.put, file, function() { - console.log(data.put); - - chatBox.onMessageSubmitted(data.put, null, file); - file = undefined; - }); - } - }); - }, - - /** - * Request upload slot from xmpp-server - * - * @param {File} file the file the user picked - * @param {Function} cb Callback after finished request - */ - requestSlot (file, cb) { - var self = this; - console.log("try sending file to: " + requestSlotUrl); - var iq = converse.env.$iq({ - to: requestSlotUrl, - type: 'get' - }).c('request', { - xmlns: Strophe.NS.HTTPUPLOAD - }).c('filename').t(file.name) - .up() - .c('size').t(file.size); - - connection.sendIQ(iq, function(stanza) { - self.successfulRequestSlotCB(stanza, cb); - }, function(stanza) { - self.failedRequestSlotCB(stanza, cb); - }); - }, - - /** - * Upload the given file to the given url. - * - * @param {String} url upload url - * @param {File} file the file the user picked - * @param {Function} success_cb callback on successful transition - */ - uploadFile (url, file, success_cb) { - console.log("uploadFile start"); - var xmlhttp = new XMLHttpRequest(); - var type = 'PUT'; - var contentType = 'application/octet-stream'; - var data = file; - var processData = false; - xmlhttp.onreadystatechange = function() { - if (xmlhttp.readyState == XMLHttpRequest.DONE) { - console.log("Status: " + xmlhttp.status); - if (xmlhttp.status == 200 || xmlhttp.status == 201) { - console.log('file successful uploaded'); - if (success_cb) { - success_cb(); - } - } - else { - console.log('error while uploading file to ' + url); - alert(__('Could not upload File please try again.')); - } - } - }; - - xmlhttp.open(type, url, true); - xmlhttp.setRequestHeader("Content-type", contentType); - xmlhttp.send(data); - - console.log("uploadFile end"); - }, - - /** - * Process successful response to slot request. - * - * @param {String} stanza - * @param {Function} cb - */ - successfulRequestSlotCB (stanza, cb) { - var slot = stanza.getElementsByTagName('slot')[0]; - - if (slot != undefined) { - var put = slot.getElementsByTagName('put')[0].textContent; - var get = slot.getElementsByTagName('get')[0].textContent; - cb({ - put: put, - get: get - }); - } else { - this.failedRequestSlotCB(stanza, cb); - } - }, - - /** - * Process failed response to slot request. - * - * @param {String} stanza - * @param {Function} cb - */ - failedRequestSlotCB (stanza, cb) { - chatBox.showHelpMessages([__('Fileupload failed')],'info'); - } - }) - } - }); - - return converse; -})); diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index d70385a57..dc5f13700 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -82,8 +82,6 @@ 'unmoderated': 'moderated' }; - Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob'); - converse.plugins.add('converse-muc-views', { /* Dependencies are other plugins which might be * overridden or relied upon, and therefore need to be loaded before @@ -697,17 +695,6 @@ msgid }); }, - sendChatRoomFile (text) { - const msgid = _converse.connection.getUniqueId(); - const stanza = $msg({ - 'from': _converse.connection.jid, - 'to': this.model.get('jid'), - 'type': 'groupchat', - 'id': msgid - }).c("body").t(text).up() - .c("x", {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(text).up(); - _converse.connection.send(stanza); - }, modifyRole(room, nick, role, reason, onSuccess, onError) { const item = $build("item", {nick, role}); @@ -757,7 +744,7 @@ * (String) text - The message text. */ if(file !== null){ - return this.sendChatRoomFile(text); + return this.model.sendChatRoomFile(text,this.model.get('jid')); } if (_converse.muc_disable_moderator_commands) { return this.sendChatRoomMessage(text); diff --git a/src/converse-muc.js b/src/converse-muc.js index 0265b1770..feaf4c1a7 100644 --- a/src/converse-muc.js +++ b/src/converse-muc.js @@ -275,6 +275,18 @@ ); }, + sendChatRoomFile (text, to) { + const msgid = _converse.connection.getUniqueId(); + const stanza = $msg({ + 'from': _converse.connection.jid, + 'to': to, + 'type': 'groupchat', + 'id': msgid + }).c("body").t(text).up() + .c("x", {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(text).up(); + _converse.connection.send(stanza); + }, + directInvite (recipient, reason) { /* Send a direct invitation as per XEP-0249 * diff --git a/src/converse.js b/src/converse.js index 6c8a48ee2..4aeae0c52 100644 --- a/src/converse.js +++ b/src/converse.js @@ -24,7 +24,7 @@ if (typeof define !== 'undefined') { "converse-minimize", // Allows chat boxes to be minimized "converse-dragresize", // Allows chat boxes to be resized by dragging them "converse-headline", // Support for headline messages - "converse-httpFileUpload", // Support for XEP-0363 + "converse-http-file-upload", // Support for XEP-0363 "converse-fullscreen" /* END: Removable components */ ], function (converse) { diff --git a/src/inverse.js b/src/inverse.js index 6ead412e9..8ee2c8136 100644 --- a/src/inverse.js +++ b/src/inverse.js @@ -25,7 +25,7 @@ if (typeof define !== 'undefined') { "converse-register", // XEP-0077 In-band registration "converse-roomslist", // Show currently open chat rooms "converse-vcard", // XEP-0054 VCard-temp - "converse-httpFileUpload", // Support for XEP-0363 + "converse-http-file-upload", // Support for XEP-0363 /* END: Removable components */ "converse-inverse", // Inverse plugin for converse.js diff --git a/tests/runner-transpiled.js b/tests/runner-transpiled.js index 06d4cb0fa..8faaebb6e 100644 --- a/tests/runner-transpiled.js +++ b/tests/runner-transpiled.js @@ -24,7 +24,7 @@ config.paths["converse-core"] = "builds/converse-core"; config.paths["converse-disco"] = "builds/converse-disco"; config.paths["converse-dragresize"] = "builds/converse-dragresize"; config.paths["converse-headline"] = "builds/converse-headline"; -config.paths["converse-httpFileUpload"]="builds/converse-httpFileUpload"; +config.paths["converse-http-file-upload"]="builds/converse-http-file-upload"; config.paths["converse-fullscreen"] = "builds/converse-fullscreen"; config.paths["converse-mam"] = "builds/converse-mam"; config.paths["converse-minimize"] = "builds/converse-minimize"; From c9a9d0111291c460a72508a354b04be4453c5065 Mon Sep 17 00:00:00 2001 From: worlword Date: Wed, 11 Apr 2018 17:09:32 +0200 Subject: [PATCH 09/12] used code from 'http-file-upload' branch. Buttons are now added to the toolbars through converse-http-file-upload.js. --- locale/de/LC_MESSAGES/converse.po | 2 +- src/converse-chatboxes.js | 3 -- src/converse-chatview.js | 13 ------ src/converse-http-file-upload.js | 62 +++++++++++++++++++++++---- src/converse-muc-views.js | 2 - src/templates/chatroom_toolbar.html | 6 --- src/templates/toolbar.html | 6 --- src/templates/toolbar_fileupload.html | 4 ++ 8 files changed, 58 insertions(+), 40 deletions(-) create mode 100644 src/templates/toolbar_fileupload.html diff --git a/locale/de/LC_MESSAGES/converse.po b/locale/de/LC_MESSAGES/converse.po index 6642b5366..24c68478a 100644 --- a/locale/de/LC_MESSAGES/converse.po +++ b/locale/de/LC_MESSAGES/converse.po @@ -146,7 +146,7 @@ msgid "has gone away" msgstr "ist jetzt abwesend" #: src/converse-chatview.js:860 -msgid "Upload a File" +msgid "Choose a file to send" msgstr "Datei versenden" #: dist/converse-no-dependencies.js:14851 diff --git a/src/converse-chatboxes.js b/src/converse-chatboxes.js index 9eff30d6a..0e12a0a17 100644 --- a/src/converse-chatboxes.js +++ b/src/converse-chatboxes.js @@ -175,9 +175,6 @@ }); }, - /** - * Upload the given file to the given url. - */ uploadFile (url, file, success_cb) { console.log("uploadFile start"); const xmlhttp = new XMLHttpRequest(); diff --git a/src/converse-chatview.js b/src/converse-chatview.js index 7b8b79746..c01fd3c56 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -250,18 +250,6 @@ 'click .toggle-spoiler': 'toggleSpoilerMessage', 'click .toggle-compose-spoiler': 'toggleComposeSpoilerMessage', 'keypress .chat-textarea': 'keyPressed', - 'click .toggle-fileUpload': 'toggleFileUpload', - 'change .fileUpload_input': 'handleFileSelect' - }, - - toggleFileUpload (ev) { - this.el.querySelector('.fileUpload_input').click(); - }, - - handleFileSelect (evt) { - var files = evt.target.files; - var file = files[0]; - this.model.sendFile(file, this); }, initialize () { @@ -382,7 +370,6 @@ 'label_clear': __('Clear all messages'), 'label_insert_smiley': __('Insert a smiley'), 'label_start_call': __('Start a call'), - 'label_upload_file': __('Upload a File'), 'label_toggle_spoiler': label_toggle_spoiler, 'show_call_button': _converse.visible_toolbar_buttons.call, 'show_spoiler_button': _converse.visible_toolbar_buttons.spoiler, diff --git a/src/converse-http-file-upload.js b/src/converse-http-file-upload.js index 34fdf1272..7578d4692 100644 --- a/src/converse-http-file-upload.js +++ b/src/converse-http-file-upload.js @@ -6,9 +6,11 @@ (function (root, factory) { define([ "converse-core", + "tpl!toolbar_fileupload" ], factory); }(this, function ( - converse + converse, + tpl_toolbar_fileupload ) { "use strict"; const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env; @@ -16,18 +18,60 @@ converse.plugins.add('converse-http-file-upload', { - dependencies: ["converse-chatboxes", "converse-chatview"], + dependencies: ["converse-chatboxes", "converse-chatview", "converse-muc-views"], + + overrides: { + ChatBoxView: { + + events: { + 'click .toggle-fileUpload': 'toggleFileUpload', + 'change .fileUpload_input': 'handleFileSelect' + }, + + addFileUploadButton (options) { + const { __ } = this.__super__._converse; + this.el.querySelector('.chat-toolbar').insertAdjacentHTML( + 'beforeend', + tpl_toolbar_fileupload({'label_upload_file': __('Choose a file to send')})); + }, + + renderToolbar (toolbar, options) { + const { _converse } = this.__super__; + const result = this.__super__.renderToolbar.apply(this, arguments); + _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, 'upload.' + _converse.domain) + .then((result) => { + if (result.supported) { + this.addFileUploadButton(); + } + }); + return result; + }, + + toggleFileUpload (ev) { + this.el.querySelector('.fileUpload_input').click(); + }, + + handleFileSelect (evt) { + var files = evt.target.files; + var file = files[0]; + this.model.sendFile(file, this); + } + }, + + ChatRoomView: { + events: { + 'click .toggle-fileUpload': 'toggleFileUpload', + 'change .fileUpload_input': 'handleFileSelect', + } + } + }, initialize () { + /* The initialize function gets called as soon as the plugin is + * loaded by converse.js's plugin machinery. + */ const { _converse } = this, { __ } = _converse; - - _converse.FileUpload = Backbone.NativeView.extend({ - /** - * Request upload slot from xmpp-server - */ - - }) } }); diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index dc5f13700..df5bbda31 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -410,8 +410,6 @@ 'click .toggle-smiley ul.emoji-picker li': 'insertEmoji', 'click .toggle-clear': 'clearChatRoomMessages', 'click .toggle-call': 'toggleCall', - 'click .toggle-fileUpload': 'toggleFileUpload', - 'change .fileUpload_input': 'handleFileSelect', 'click .toggle-occupants': 'toggleOccupants', 'click .new-msgs-indicator': 'viewUnreadMessages', 'click .occupant': 'onOccupantClicked', diff --git a/src/templates/chatroom_toolbar.html b/src/templates/chatroom_toolbar.html index 62704930b..cc3b83b3d 100644 --- a/src/templates/chatroom_toolbar.html +++ b/src/templates/chatroom_toolbar.html @@ -4,12 +4,6 @@
  • {[ } ]} -{[ if (o.show_fileUpload_button) { ]} - -
  • - -
  • -{[ } ]} {[ if (o.show_call_button) { ]}
  • {[ } ]} diff --git a/src/templates/toolbar.html b/src/templates/toolbar.html index d30008a23..3dca527ff 100644 --- a/src/templates/toolbar.html +++ b/src/templates/toolbar.html @@ -3,12 +3,6 @@ {[ } ]} -{[ if (o.show_fileUpload_button) { ]} - -
  • - -
  • -{[ } ]} {[ if (o.show_call_button) { ]}
  • {[ } ]} diff --git a/src/templates/toolbar_fileupload.html b/src/templates/toolbar_fileupload.html new file mode 100644 index 000000000..ba585afdb --- /dev/null +++ b/src/templates/toolbar_fileupload.html @@ -0,0 +1,4 @@ + +
  • + +
  • \ No newline at end of file From bf3e6f23630d9a5143036b89a65d20e4dcae5bb7 Mon Sep 17 00:00:00 2001 From: worlword Date: Thu, 12 Apr 2018 09:24:41 +0200 Subject: [PATCH 10/12] some code-changes to align with the coding standard --- src/converse-chatboxes.js | 2 +- src/converse-chatview.js | 7 +++---- src/converse-muc-views.js | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/converse-chatboxes.js b/src/converse-chatboxes.js index 19c3a5195..21a717016 100644 --- a/src/converse-chatboxes.js +++ b/src/converse-chatboxes.js @@ -116,7 +116,7 @@ }); }, - createFileMessageStanza(message, to){ + createFileMessageStanza (message, to) { const stanza = $msg({ 'from': _converse.connection.jid, 'to': to, diff --git a/src/converse-chatview.js b/src/converse-chatview.js index e2423a223..b5ce23740 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -255,7 +255,7 @@ 'click .toggle-smiley': 'toggleEmojiMenu', 'click .toggle-spoiler': 'toggleSpoilerMessage', 'click .toggle-compose-spoiler': 'toggleComposeSpoilerMessage', - 'keypress .chat-textarea': 'keyPressed', + 'keypress .chat-textarea': 'keyPressed' }, initialize () { @@ -829,7 +829,7 @@ // TODO: We might want to send to specfic resources. // Especially in the OTR case. var messageStanza; - if(file !== null){ + if (file !== null) { messageStanza = this.createFileMessageStanza(message, this.model.get('jid')); } else { @@ -845,7 +845,7 @@ 'xmns': Strophe.NS.DELAY, 'stamp': moment().format() }).up() - .cnode(message.tree()) + .cnode(messageStanza.tree()) ); } }, @@ -890,7 +890,6 @@ } const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint); const message = this.model.messages.create(attrs); - this.sendMessage(message, file); }, diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index 41ab4fafc..45d426ce7 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -835,7 +835,7 @@ * Parameters: * (String) text - The message text. */ - if(file !== null){ + if (file !== null) { return this.model.sendChatRoomFile(text,this.model.get('jid')); } if (_converse.muc_disable_moderator_commands) { From ca40cf6541e641f2ff9a3112714c82bd530d9667 Mon Sep 17 00:00:00 2001 From: worlword Date: Thu, 12 Apr 2018 09:37:58 +0200 Subject: [PATCH 11/12] removed EmptyLines in ChatView.js and Change Lineendings to LF in toolbar_fileUpload.html --- src/converse-chatview.js | 4 ---- src/templates/toolbar_fileupload.html | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/converse-chatview.js b/src/converse-chatview.js index b5ce23740..fe7cd2cf2 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -818,8 +818,6 @@ return stanza; }, - - sendMessage (message, file=null) { /* Responsible for sending off a text message. * @@ -1247,13 +1245,11 @@ } }); - _converse.on('connected', () => { // Advertise that we support XEP-0382 Message Spoilers _converse.connection.disco.addFeature(Strophe.NS.SPOILER); }); - /************************ BEGIN API ************************/ _.extend(_converse.api, { 'chatviews': { diff --git a/src/templates/toolbar_fileupload.html b/src/templates/toolbar_fileupload.html index ba585afdb..0d8997183 100644 --- a/src/templates/toolbar_fileupload.html +++ b/src/templates/toolbar_fileupload.html @@ -1,4 +1,4 @@ - -
  • - -
  • \ No newline at end of file + +
  • + +
  • \ No newline at end of file From 3b1d27636d8a373d378994f106bcbc4354bacce9 Mon Sep 17 00:00:00 2001 From: worlword Date: Thu, 12 Apr 2018 19:44:52 +0200 Subject: [PATCH 12/12] problem fixed, that file-upload would not start. Also did some code cleanup --- src/converse-chatboxes.js | 24 ++++-------------------- src/converse-chatview.js | 8 +++----- src/converse-http-file-upload.js | 26 ++++++++------------------ src/templates/toolbar_fileupload.html | 2 +- 4 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/converse-chatboxes.js b/src/converse-chatboxes.js index 21a717016..054b95fca 100644 --- a/src/converse-chatboxes.js +++ b/src/converse-chatboxes.js @@ -131,22 +131,16 @@ sendFile (file, chatbox) { const self = this; - console.log('Send file via http upload'); const request_slot_url = 'upload.' + _converse.domain; _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, request_slot_url) .then((result) => { chatbox.showHelpMessages([__('The file upload starts now')],'info'); self.requestSlot(file, request_slot_url, function(data) { if (!data) { - // general error - console.log('Unknown error while requesting upload slot.'); alert(__('File upload failed. Please check the log.')); } else if (data.error) { - // specific error - console.log('The XMPP-Server return an error of the type: ' + data.error.type); alert(__('File upload failed. Please check the log.')); } else if (data.get && data.put) { - console.log('slot received, start upload to ' + data.put); self.uploadFile(data.put, file, function() { console.log(data.put); chatbox.onMessageSubmitted(data.put, null, file); @@ -175,7 +169,7 @@ }); }, - uploadFile (url, file, success_cb) { + uploadFile (url, file, callback) { console.log("uploadFile start"); const xmlhttp = new XMLHttpRequest(); const contentType = 'application/octet-stream'; @@ -183,13 +177,11 @@ if (xmlhttp.readyState === XMLHttpRequest.DONE) { console.log("Status: " + xmlhttp.status); if (xmlhttp.status === 200 || xmlhttp.status === 201) { - console.log('file successful uploaded'); - if (success_cb) { - success_cb(); + if (callback) { + callback(); } } else { - console.log('error while uploading file to ' + url); alert(__('Could not upload File please try again.')); } } @@ -198,13 +190,8 @@ xmlhttp.open('PUT', url, true); xmlhttp.setRequestHeader("Content-type", contentType); xmlhttp.send(file); - - console.log("uploadFile end"); }, - - /** - * Process successful response to slot request. - */ + successfulRequestSlotCB (stanza, cb) { const slot = stanza.getElementsByTagName('slot')[0]; @@ -220,9 +207,6 @@ } }, - /** - * Process failed response to slot request. - */ failedRequestSlotCB (stanza, cb) { alert(__('Could not upload File please try again.')); }, diff --git a/src/converse-chatview.js b/src/converse-chatview.js index fe7cd2cf2..1e6389476 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -116,8 +116,7 @@ 'call': false, 'clear': true, 'emoji': true, - 'spoiler': true, - 'fileUpload': true + 'spoiler': true }, }); emojione.imagePathPNG = _converse.emojione_image_path; @@ -379,7 +378,6 @@ 'label_toggle_spoiler': label_toggle_spoiler, 'show_call_button': _converse.visible_toolbar_buttons.call, 'show_spoiler_button': _converse.visible_toolbar_buttons.spoiler, - 'show_fileUpload_button': _converse.visible_toolbar_buttons.fileUpload, 'use_emoji': _converse.visible_toolbar_buttons.emoji, }); }, @@ -818,7 +816,7 @@ return stanza; }, - sendMessage (message, file=null) { + sendMessage (message, file = null) { /* Responsible for sending off a text message. * * Parameters: @@ -828,7 +826,7 @@ // Especially in the OTR case. var messageStanza; if (file !== null) { - messageStanza = this.createFileMessageStanza(message, this.model.get('jid')); + messageStanza = this.model.createFileMessageStanza(message, this.model.get('jid')); } else { messageStanza = this.createMessageStanza(message); diff --git a/src/converse-http-file-upload.js b/src/converse-http-file-upload.js index 7578d4692..6e0e0f7f3 100644 --- a/src/converse-http-file-upload.js +++ b/src/converse-http-file-upload.js @@ -22,7 +22,6 @@ overrides: { ChatBoxView: { - events: { 'click .toggle-fileUpload': 'toggleFileUpload', 'change .fileUpload_input': 'handleFileSelect' @@ -35,18 +34,6 @@ tpl_toolbar_fileupload({'label_upload_file': __('Choose a file to send')})); }, - renderToolbar (toolbar, options) { - const { _converse } = this.__super__; - const result = this.__super__.renderToolbar.apply(this, arguments); - _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, 'upload.' + _converse.domain) - .then((result) => { - if (result.supported) { - this.addFileUploadButton(); - } - }); - return result; - }, - toggleFileUpload (ev) { this.el.querySelector('.fileUpload_input').click(); }, @@ -55,21 +42,24 @@ var files = evt.target.files; var file = files[0]; this.model.sendFile(file, this); - } + }, + + renderToolbar (toolbar, options) { + const result = this.__super__.renderToolbar.apply(this, arguments); + this.addFileUploadButton(); + return result; + }, }, ChatRoomView: { events: { 'click .toggle-fileUpload': 'toggleFileUpload', - 'change .fileUpload_input': 'handleFileSelect', + 'change .fileUpload_input': 'handleFileSelect' } } }, initialize () { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ const { _converse } = this, { __ } = _converse; } diff --git a/src/templates/toolbar_fileupload.html b/src/templates/toolbar_fileupload.html index 0d8997183..691d19011 100644 --- a/src/templates/toolbar_fileupload.html +++ b/src/templates/toolbar_fileupload.html @@ -1,4 +1,4 @@
  • -
  • \ No newline at end of file +