Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
75ae76ad
Commit
75ae76ad
authored
Mar 28, 2019
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use `_converse.AutoComplete` in "Add Contact" modal
parent
f0848c28
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
201 additions
and
187 deletions
+201
-187
css/converse.css
css/converse.css
+4
-4
dist/converse.js
dist/converse.js
+104
-103
sass/_awesomplete.scss
sass/_awesomplete.scss
+6
-5
spec/controlbox.js
spec/controlbox.js
+13
-15
src/converse-autocomplete.js
src/converse-autocomplete.js
+32
-28
src/converse-headline.js
src/converse-headline.js
+1
-1
src/converse-rosterview.js
src/converse-rosterview.js
+24
-24
src/templates/add_contact_modal.html
src/templates/add_contact_modal.html
+17
-7
No files found.
css/converse.css
View file @
75ae76ad
...
...
@@ -12198,14 +12198,14 @@ body.converse-fullscreen {
display
:
none
;
}
#conversejs
.suggestion-box
.suggestion-box__results--above
:after
,
#conversejs
.awesomplete
.suggestion-box__results--above
:after
{
z-index
:
1
;
z-index
:
-
1
;
content
:
""
;
position
:
absolute
;
bottom
:
-.43em
;
bottom
:
-
0
.43em
;
left
:
1em
;
width
:
0
;
height
:
0
;
padding
:
.4em
;
padding
:
0
.4em
;
background
:
white
;
border
:
inherit
;
border-left
:
0
;
...
...
@@ -12252,7 +12252,7 @@ body.converse-fullscreen {
bottom
:
4.5em
;
}
#conversejs
.converse-overlayed
.suggestion-box__results--above
{
bottom
:
5
.5em
;
}
bottom
:
3
.5em
;
}
#conversejs
.converse-embedded
{
-webkit-box-sizing
:
border-box
;
...
...
dist/converse.js
View file @
75ae76ad
...
...
@@ -48035,6 +48035,34 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
return element;
};
class Suggestion extends String {
constructor(data) {
super();
const o = Array.isArray(data) ? {
label: data[0],
value: data[1]
} : typeof data === "object" && "label" in data && "value" in data ? data : {
label: data,
value: data
};
this.label = o.label || o.value;
this.value = o.value;
}
get lenth() {
return this.label.length;
}
toString() {
return "" + this.label;
}
valueOf() {
return this.toString();
}
}
class AutoComplete {
constructor(el) {
let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
...
...
@@ -48062,6 +48090,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
'min_chars': 2,
'max_items': 10,
'auto_evaluate': true,
// Should evaluation happen automatically without any particular key as trigger?
'auto_first': false,
// Should the first element be automatically selected?
'data': _.identity,
...
...
@@ -48121,7 +48150,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
if (list && list.children) {
const items = [];
slice.apply(list.children).forEach(function (el) {
Array.prototype.
slice.apply(list.children).forEach(function (el) {
if (!el.disabled) {
const text = el.textContent.trim(),
value = el.value || text,
...
...
@@ -48230,7 +48259,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
}
}
select(selected
, origin
) {
select(selected) {
if (selected) {
this.index = u.siblingIndex(selected);
} else {
...
...
@@ -48311,9 +48340,9 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
}
evaluate(ev) {
const
arrow_pressed = ev.keyCode === _converse.keycodes.UP_ARROW || ev.keyCode === _converse.keycodes.DOWN_ARROW
;
const
selecting = this.selected && ev && (ev.keyCode === _converse.keycodes.UP_ARROW || ev.keyCode === _converse.keycodes.DOWN_ARROW)
;
if (!this.auto_
completing || this.selected && arrow_pressed
) {
if (!this.auto_
evaluate && !this.auto_completing || selecting
) {
return;
}
...
...
@@ -48362,33 +48391,8 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
} // Make it an event emitter
_.extend(AutoComplete.prototype, Backbone.Events); // Private functions
function Suggestion(data) {
const o = Array.isArray(data) ? {
label: data[0],
value: data[1]
} : typeof data === "object" && "label" in data && "value" in data ? data : {
label: data,
value: data
};
this.label = o.label || o.value;
this.value = o.value;
}
Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", {
get: function get() {
return this.label.length;
}
});
Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () {
return "" + this.label;
}; // Helpers
_.extend(AutoComplete.prototype, Backbone.Events);
var slice = Array.prototype.slice;
const helpers = {
getElement(expr, el) {
return typeof expr === "string" ? (el || document).querySelector(expr) : expr || null;
...
...
@@ -52193,7 +52197,7 @@ __webpack_require__.r(__webpack_exports__);
// Converse.js (A browser based XMPP chat client)
// https://conversejs.org
//
// Copyright (c) 201
2-2017
, Jan-Carel Brand <jc@opkode.com>
// Copyright (c) 201
9
, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
...
...
@@ -59435,27 +59439,25 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _converse_headless_converse_roster__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-roster */ "./src/headless/converse-roster.js");
/* harmony import */ var _converse_headless_converse_chatboxes__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-chatboxes */ "./src/headless/converse-chatboxes.js");
/* harmony import */ var converse_modal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! converse-modal */ "./src/converse-modal.js");
/* harmony import */ var awesomplete__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! awesomplete */ "./node_modules/awesomplete-avoid-xss/awesomplete.js");
/* harmony import */ var awesomplete__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(awesomplete__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var formdata_polyfill__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! formdata-polyfill */ "./node_modules/formdata-polyfill/FormData.js");
/* harmony import */ var formdata_polyfill__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(formdata_polyfill__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
/* harmony import */ var templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! templates/add_contact_modal.html */ "./src/templates/add_contact_modal.html");
/* harmony import */ var templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var templates_group_header_html__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! templates/group_header.html */ "./src/templates/group_header.html");
/* harmony import */ var templates_group_header_html__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(templates_group_header_html__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! templates/pending_contact.html */ "./src/templates/pending_contact.html");
/* harmony import */ var templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! templates/requesting_contact.html */ "./src/templates/requesting_contact.html");
/* harmony import */ var templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_9__);
/* harmony import */ var templates_roster_html__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! templates/roster.html */ "./src/templates/roster.html");
/* harmony import */ var templates_roster_html__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(templates_roster_html__WEBPACK_IMPORTED_MODULE_10__);
/* harmony import */ var templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! templates/roster_filter.html */ "./src/templates/roster_filter.html");
/* harmony import */ var templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_11__);
/* harmony import */ var templates_roster_item_html__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! templates/roster_item.html */ "./src/templates/roster_item.html");
/* harmony import */ var templates_roster_item_html__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(templates_roster_item_html__WEBPACK_IMPORTED_MODULE_12__);
/* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! templates/search_contact.html */ "./src/templates/search_contact.html");
/* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__);
/* harmony import */ var formdata_polyfill__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! formdata-polyfill */ "./node_modules/formdata-polyfill/FormData.js");
/* harmony import */ var formdata_polyfill__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(formdata_polyfill__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
/* harmony import */ var templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/add_contact_modal.html */ "./src/templates/add_contact_modal.html");
/* harmony import */ var templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var templates_group_header_html__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! templates/group_header.html */ "./src/templates/group_header.html");
/* harmony import */ var templates_group_header_html__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(templates_group_header_html__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! templates/pending_contact.html */ "./src/templates/pending_contact.html");
/* harmony import */ var templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! templates/requesting_contact.html */ "./src/templates/requesting_contact.html");
/* harmony import */ var templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var templates_roster_html__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! templates/roster.html */ "./src/templates/roster.html");
/* harmony import */ var templates_roster_html__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(templates_roster_html__WEBPACK_IMPORTED_MODULE_9__);
/* harmony import */ var templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! templates/roster_filter.html */ "./src/templates/roster_filter.html");
/* harmony import */ var templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_10__);
/* harmony import */ var templates_roster_item_html__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! templates/roster_item.html */ "./src/templates/roster_item.html");
/* harmony import */ var templates_roster_item_html__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(templates_roster_item_html__WEBPACK_IMPORTED_MODULE_11__);
/* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! templates/search_contact.html */ "./src/templates/search_contact.html");
/* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(templates_search_contact_html__WEBPACK_IMPORTED_MODULE_12__);
// Converse.js
// https://conversejs.org
//
...
...
@@ -59474,16 +59476,15 @@ __webpack_require__.r(__webpack_exports__);
const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].env,
const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].env,
Backbone = _converse$env.Backbone,
Strophe = _converse$env.Strophe,
$iq = _converse$env.$iq,
b64_sha1 = _converse$env.b64_sha1,
sizzle = _converse$env.sizzle,
_ = _converse$env._;
const u = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].env.utils;
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].plugins.add('converse-rosterview', {
const u = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].env.utils;
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].plugins.add('converse-rosterview', {
dependencies: ["converse-roster", "converse-modal"],
overrides: {
// Overrides mentioned here will be picked up by converse.js's
...
...
@@ -59588,7 +59589,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
toHTML() {
const label_nickname = _converse.xhr_user_search_url ? __('Contact name') : __('Optional nickname');
return templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_
6
___default()(_.extend(this.model.toJSON(), {
return templates_add_contact_modal_html__WEBPACK_IMPORTED_MODULE_
5
___default()(_.extend(this.model.toJSON(), {
'_converse': _converse,
'heading_new_contact': __('Add a Contact'),
'label_xmpp_address': __('XMPP Address'),
...
...
@@ -59601,55 +59602,52 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
afterRender() {
if (_converse.xhr_user_search_url && _.isString(_converse.xhr_user_search_url)) {
this.initXHRAutoComplete(
this.el
);
this.
el.addEventListener('awesomplete
-selectcomplete', ev => {
this.initXHRAutoComplete();
this.
name_auto_complete.on('suggestion-box
-selectcomplete', ev => {
this.el.querySelector('input[name="name"]').value = ev.text.label;
this.el.querySelector('input[name="jid"]').value = ev.text.value;
});
} else {
this.initJIDAutoComplete(
this.el
);
this.initJIDAutoComplete();
}
const jid_input = this.el.querySelector('input[name="jid"]');
this.el.addEventListener('shown.bs.modal', () => jid_input.focus(), false);
},
initJIDAutoComplete(root) {
const jid_input = root.querySelector('input[name="jid"]');
const list = _.uniq(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))));
new awesomplete__WEBPACK_IMPORTED_MODULE_3___default.a(jid_input, {
'list': list,
initJIDAutoComplete() {
const el = this.el.querySelector('.suggestion-box__jid').parentElement;
this.jid_auto_complete = new _converse.AutoComplete(el, {
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
'filter': awesomplete__WEBPACK_IMPORTED_MODULE_3___default.a.FILTER_STARTSWITH
'filter': _converse.FILTER_STARTSWITH,
'list': _.uniq(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))
});
},
initXHRAutoComplete(
root
) {
const
name_input = this.el.querySelector('input[name="name"]')
;
const jid_input = this.el.querySelector('input[name="jid"]');
const awesomplete = new awesomplete__WEBPACK_IMPORTED_MODULE_3___default.a(name_input, {
'
minChars': 1
,
initXHRAutoComplete() {
const
el = this.el.querySelector('.suggestion-box__name').parentElement
;
this.name_auto_complete = new _converse.AutoComplete(el, {
'auto_evaluate': false,
'
filter': _converse.FILTER_STARTSWITH
,
'list': []
});
const xhr = new window.XMLHttpRequest(); // `open` must be called after `onload` for mock/testing purposes.
xhr.onload =
function ()
{
xhr.onload =
() =>
{
if (xhr.responseText) {
awesomplete.list = JSON.parse(xhr.responseText).map(i => {
//eslint-disable-line arrow-body-style
return {
const r = xhr.responseText;
this.name_auto_complete.list = JSON.parse(r).map(i => ({
'label': i.fullname || i.jid,
'value': i.jid
}
;
})
;
awes
omplete.evaluate();
}))
;
this.name_auto_complete.auto_completing = true
;
this.name_auto_c
omplete.evaluate();
}
};
name_input.addEventListener('input', _.debounce(() => {
xhr.open("GET", `${_converse.xhr_user_search_url}q=${name_input.value}`, true);
const input_el = this.el.querySelector('input[name="name"]');
input_el.addEventListener('input', _.debounce(() => {
xhr.open("GET", `${_converse.xhr_user_search_url}q=${input_el.value}`, true);
xhr.send();
}, 300));
},
...
...
@@ -59661,9 +59659,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
name = data.get('name');
if (!jid || _.compact(jid.split('@')).length < 2) {
// XXX: we have to do this manually, instead of via
// XXX: we
used to
have to do this manually, instead of via
// toHTML because Awesomplete messes things up and
// confuses Snabbdom
// We now use _converse.AutoComplete, can this be removed?
u.addClass('is-invalid', this.el.querySelector('input[name="jid"]'));
u.addClass('d-block', this.el.querySelector('.invalid-feedback'));
} else {
...
...
@@ -59704,7 +59703,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
},
toHTML() {
return templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_1
1
___default()(_.extend(this.model.toJSON(), {
return templates_roster_filter_html__WEBPACK_IMPORTED_MODULE_1
0
___default()(_.extend(this.model.toJSON(), {
visible: this.shouldBeVisible(),
placeholder: __('Filter'),
title_contact_filter: __('Filter by contact name'),
...
...
@@ -59896,7 +59895,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
*/
const display_name = this.model.getDisplayName();
this.el.classList.add('pending-xmpp-contact');
this.el.innerHTML = templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_
8
___default()(_.extend(this.model.toJSON(), {
this.el.innerHTML = templates_pending_contact_html__WEBPACK_IMPORTED_MODULE_
7
___default()(_.extend(this.model.toJSON(), {
'display_name': display_name,
'desc_remove': __('Click to remove %1$s as a contact', display_name),
'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts
...
...
@@ -59904,7 +59903,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
} else if (requesting === true) {
const display_name = this.model.getDisplayName();
this.el.classList.add('requesting-xmpp-contact');
this.el.innerHTML = templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_
9
___default()(_.extend(this.model.toJSON(), {
this.el.innerHTML = templates_requesting_contact_html__WEBPACK_IMPORTED_MODULE_
8
___default()(_.extend(this.model.toJSON(), {
'display_name': display_name,
'desc_accept': __("Click to accept the contact request from %1$s", display_name),
'desc_decline': __("Click to decline the contact request from %1$s", display_name),
...
...
@@ -59951,7 +59950,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
}
const display_name = item.getDisplayName();
this.el.innerHTML = templates_roster_item_html__WEBPACK_IMPORTED_MODULE_1
2
___default()(_.extend(item.toJSON(), {
this.el.innerHTML = templates_roster_item_html__WEBPACK_IMPORTED_MODULE_1
1
___default()(_.extend(item.toJSON(), {
'display_name': display_name,
'desc_status': STATUSES[show],
'status_icon': status_icon,
...
...
@@ -60077,7 +60076,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
render() {
this.el.setAttribute('data-group', this.model.get('name'));
this.el.innerHTML = templates_group_header_html__WEBPACK_IMPORTED_MODULE_
7
___default()({
this.el.innerHTML = templates_group_header_html__WEBPACK_IMPORTED_MODULE_
6
___default()({
'label_group': this.model.get('name'),
'desc_group_toggle': this.model.get('description'),
'toggle_state': this.model.get('state'),
...
...
@@ -60309,7 +60308,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
},
render() {
this.el.innerHTML = templates_roster_html__WEBPACK_IMPORTED_MODULE_
10
___default()({
this.el.innerHTML = templates_roster_html__WEBPACK_IMPORTED_MODULE_
9
___default()({
'allow_contact_requests': _converse.allow_contact_requests,
'heading_contacts': __('Contacts'),
'title_add_contact': __('Add a contact'),
...
...
@@ -60554,11 +60553,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
_converse.api.listen.on('rosterReadyAfterReconnection', initRoster);
_converse.api.listen.on('afterTearDown', () => {
if (_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].rosterview) {
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].rosterview.model.off().reset();
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].rosterview.each(groupview => groupview.removeAll().remove());
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].rosterview.removeAll().remove();
delete _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
5
__["default"].rosterview;
if (_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].rosterview) {
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].rosterview.model.off().reset();
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].rosterview.each(groupview => groupview.removeAll().remove());
_converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].rosterview.removeAll().remove();
delete _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_
4
__["default"].rosterview;
}
});
}
...
...
@@ -92644,19 +92643,21 @@ __p += ' hidden ';
} ;
__p += '">\n <label class="clearfix" for="jid">' +
__e(o.label_xmpp_address) +
':</label>\n <input type="text" name="jid" required="required" value="' +
':</label>\n <
div class="suggestion-box suggestion-box__jid">\n <ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>\n <
input type="text" name="jid" required="required" value="' +
__e(o.jid) +
'"\n
class="form-control"\n
placeholder="' +
'"\n
class="form-control suggestion-box__input"\n
placeholder="' +
__e(o.contact_placeholder) +
'"/>\n <div class="invalid-feedback">' +
__e(o.error_message) +
'</div>\n </div>\n <div class="form-group">\n <label class="clearfix" for="name">' +
'</div>\n
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>\n </div>\n
</div>\n <div class="form-group">\n <label class="clearfix" for="name">' +
__e(o.label_nickname) +
':</label>\n <input type="text" name="name" value="' +
':</label>\n <
div class="suggestion-box suggestion-box__name">\n <ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>\n <
input type="text" name="name" value="' +
__e(o.nickname) +
'"\n
class="form-control"\n
placeholder="' +
'"\n
class="form-control suggestion-box__input"\n
placeholder="' +
__e(o.nickname_placeholder) +
'"/>\n </div>\n <button type="submit" class="btn btn-primary">' +
'"/>\n <div class="invalid-feedback">' +
__e(o.error_message) +
'</div>\n <span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>\n </div>\n\n </div>\n <button type="submit" class="btn btn-primary">' +
__e(o.label_add) +
'</button>\n </div>\n </form>\n </div>\n </div>\n</div>\n';
return __p
sass/_awesomplete.scss
View file @
75ae76ad
...
...
@@ -71,13 +71,14 @@
display
:
none
;
}
&
:after
{
z-index
:
1
;
z-index
:
-
1
;
content
:
""
;
position
:
absolute
;
bottom
:
-.43em
;
bottom
:
-
0
.43em
;
left
:
1em
;
width
:
0
;
height
:
0
;
padding
:
.4em
;
width
:
0
;
height
:
0
;
padding
:
0
.4em
;
background
:
white
;
border
:
inherit
;
border-left
:
0
;
...
...
@@ -140,6 +141,6 @@
#conversejs
.converse-overlayed
{
.suggestion-box__results--above
{
bottom
:
5
.5em
;
bottom
:
3
.5em
;
}
}
spec/controlbox.js
View file @
75ae76ad
...
...
@@ -207,7 +207,7 @@
input_jid
.
value
=
'
someone@
'
;
const
evt
=
new
Event
(
'
input
'
);
input_jid
.
dispatchEvent
(
evt
);
expect
(
modal
.
el
.
querySelector
(
'
.
awesomplete
li
'
).
textContent
).
toBe
(
'
someone@localhost
'
);
expect
(
modal
.
el
.
querySelector
(
'
.
suggestion-box
li
'
).
textContent
).
toBe
(
'
someone@localhost
'
);
input_jid
.
value
=
'
someone@localhost
'
;
input_name
.
value
=
'
Someone
'
;
modal
.
el
.
querySelector
(
'
button[type="submit"]
'
).
click
();
...
...
@@ -246,30 +246,28 @@
cbview
.
el
.
querySelector
(
'
.add-contact
'
).
click
()
const
modal
=
_converse
.
rosterview
.
add_contact_modal
;
await
test_utils
.
waitUntil
(()
=>
u
.
isVisible
(
modal
.
el
),
1000
);
// We only have autocomplete for the name input
expect
(
modal
.
jid_auto_complete
).
toBe
(
undefined
);
expect
(
modal
.
name_auto_complete
instanceof
_converse
.
AutoComplete
).
toBe
(
true
);
const
input_el
=
modal
.
el
.
querySelector
(
'
input[name="name"]
'
);
input_el
.
value
=
'
marty
'
;
let
evt
=
new
Event
(
'
input
'
);
input_el
.
dispatchEvent
(
evt
);
await
test_utils
.
waitUntil
(()
=>
modal
.
el
.
querySelector
(
'
.awesomplete li
'
),
1000
);
input_el
.
dispatchEvent
(
new
Event
(
'
input
'
));
await
test_utils
.
waitUntil
(()
=>
modal
.
el
.
querySelector
(
'
.suggestion-box li
'
),
1000
);
const
sendIQ
=
_converse
.
connection
.
sendIQ
;
let
sent_stanza
,
IQ_id
;
spyOn
(
_converse
.
connection
,
'
sendIQ
'
).
and
.
callFake
(
function
(
iq
,
callback
,
errback
)
{
sent_stanza
=
iq
;
IQ_id
=
sendIQ
.
bind
(
this
)(
iq
,
callback
,
errback
);
});
expect
(
modal
.
el
.
querySelectorAll
(
'
.
awesomplete
li
'
).
length
).
toBe
(
1
);
const
suggestion
=
modal
.
el
.
querySelector
(
'
.
awesomplete
li
'
);
expect
(
modal
.
el
.
querySelectorAll
(
'
.
suggestion-box
li
'
).
length
).
toBe
(
1
);
const
suggestion
=
modal
.
el
.
querySelector
(
'
.
suggestion-box
li
'
);
expect
(
suggestion
.
textContent
).
toBe
(
'
Marty McFly
'
);
// Can't trigger "mousedown" event so trigger the Awesomplete
// custom event which would have been triggered upon mousedown.
evt
=
document
.
createEvent
(
"
HTMLEvents
"
);
evt
.
initEvent
(
'
awesomplete-selectcomplete
'
,
true
,
true
);
evt
.
text
=
{
'
label
'
:
'
Marty McFly
'
,
'
value
'
:
'
marty@mcfly.net
'
}
modal
.
el
.
dispatchEvent
(
evt
);
// Mock selection
modal
.
name_auto_complete
.
select
(
suggestion
);
expect
(
input_el
.
value
).
toBe
(
'
Marty McFly
'
);
expect
(
modal
.
el
.
querySelector
(
'
input[name="jid"]
'
).
value
).
toBe
(
'
marty@mcfly.net
'
);
modal
.
el
.
querySelector
(
'
button[type="submit"]
'
).
click
();
...
...
src/converse-autocomplete.js
View file @
75ae76ad
...
...
@@ -53,6 +53,32 @@ converse.plugins.add("converse-autocomplete", {
};
class
Suggestion
extends
String
{
constructor
(
data
)
{
super
();
const
o
=
Array
.
isArray
(
data
)
?
{
label
:
data
[
0
],
value
:
data
[
1
]
}
:
typeof
data
===
"
object
"
&&
"
label
"
in
data
&&
"
value
"
in
data
?
data
:
{
label
:
data
,
value
:
data
};
this
.
label
=
o
.
label
||
o
.
value
;
this
.
value
=
o
.
value
;
}
get
lenth
()
{
return
this
.
label
.
length
;
}
toString
()
{
return
""
+
this
.
label
;
}
valueOf
()
{
return
this
.
toString
();
}
}
class
AutoComplete
{
constructor
(
el
,
config
=
{})
{
...
...
@@ -76,7 +102,7 @@ converse.plugins.add("converse-autocomplete", {
'
include_triggers
'
:
[],
// Array of trigger keys which should be included in the returned value
'
min_chars
'
:
2
,
'
max_items
'
:
10
,
'
auto_evaluate
'
:
true
,
'
auto_evaluate
'
:
true
,
// Should evaluation happen automatically without any particular key as trigger?
'
auto_first
'
:
false
,
// Should the first element be automatically selected?
'
data
'
:
_
.
identity
,
'
filter
'
:
_converse
.
FILTER_CONTAINS
,
...
...
@@ -129,7 +155,7 @@ converse.plugins.add("converse-autocomplete", {
list
=
helpers
.
getElement
(
list
);
if
(
list
&&
list
.
children
)
{
const
items
=
[];
slice
.
apply
(
list
.
children
).
forEach
(
function
(
el
)
{
Array
.
prototype
.
slice
.
apply
(
list
.
children
).
forEach
(
function
(
el
)
{
if
(
!
el
.
disabled
)
{
const
text
=
el
.
textContent
.
trim
(),
value
=
el
.
value
||
text
,
...
...
@@ -230,7 +256,7 @@ converse.plugins.add("converse-autocomplete", {
}
}
select
(
selected
,
origin
)
{
select
(
selected
)
{
if
(
selected
)
{
this
.
index
=
u
.
siblingIndex
(
selected
);
}
else
{
...
...
@@ -305,11 +331,11 @@ converse.plugins.add("converse-autocomplete", {
}
evaluate
(
ev
)
{
const
arrow_pressed
=
(
const
selecting
=
this
.
selected
&&
ev
&&
(
ev
.
keyCode
===
_converse
.
keycodes
.
UP_ARROW
||
ev
.
keyCode
===
_converse
.
keycodes
.
DOWN_ARROW
);
if
(
!
this
.
auto_
completing
||
(
this
.
selected
&&
arrow_pressed
)
)
{
if
(
!
this
.
auto_
evaluate
&&
!
this
.
auto_completing
||
selecting
)
{
return
;
}
...
...
@@ -339,7 +365,7 @@ converse.plugins.add("converse-autocomplete", {
this
.
suggestions
=
this
.
suggestions
.
sort
(
this
.
sort
);
}
this
.
suggestions
=
this
.
suggestions
.
slice
(
0
,
this
.
max_items
);
this
.
suggestions
.
forEach
(
(
text
)
=>
this
.
ul
.
appendChild
(
this
.
item
(
text
,
value
)));
this
.
suggestions
.
forEach
(
text
=>
this
.
ul
.
appendChild
(
this
.
item
(
text
,
value
)));
if
(
this
.
ul
.
children
.
length
===
0
)
{
this
.
close
({
'
reason
'
:
'
nomatches
'
});
...
...
@@ -357,28 +383,6 @@ converse.plugins.add("converse-autocomplete", {
_
.
extend
(
AutoComplete
.
prototype
,
Backbone
.
Events
);
// Private functions
function
Suggestion
(
data
)
{
const
o
=
Array
.
isArray
(
data
)
?
{
label
:
data
[
0
],
value
:
data
[
1
]
}
:
typeof
data
===
"
object
"
&&
"
label
"
in
data
&&
"
value
"
in
data
?
data
:
{
label
:
data
,
value
:
data
};
this
.
label
=
o
.
label
||
o
.
value
;
this
.
value
=
o
.
value
;
}
Object
.
defineProperty
(
Suggestion
.
prototype
=
Object
.
create
(
String
.
prototype
),
"
length
"
,
{
get
:
function
()
{
return
this
.
label
.
length
;
}
});
Suggestion
.
prototype
.
toString
=
Suggestion
.
prototype
.
valueOf
=
function
()
{
return
""
+
this
.
label
;
};
// Helpers
var
slice
=
Array
.
prototype
.
slice
;
const
helpers
=
{
getElement
(
expr
,
el
)
{
...
...
src/converse-headline.js
View file @
75ae76ad
// Converse.js (A browser based XMPP chat client)
// https://conversejs.org
//
// Copyright (c) 201
2-2017
, Jan-Carel Brand <jc@opkode.com>
// Copyright (c) 201
9
, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
import
"
converse-chatview
"
;
...
...
src/converse-rosterview.js
View file @
75ae76ad
...
...
@@ -7,7 +7,6 @@
import
"
@converse/headless/converse-roster
"
;
import
"
@converse/headless/converse-chatboxes
"
;
import
"
converse-modal
"
;
import
Awesomplete
from
"
awesomplete
"
;
import
_FormData
from
"
formdata-polyfill
"
;
import
converse
from
"
@converse/headless/converse-core
"
;
import
tpl_add_contact_modal
from
"
templates/add_contact_modal.html
"
;
...
...
@@ -132,47 +131,47 @@ converse.plugins.add('converse-rosterview', {
afterRender
()
{
if
(
_converse
.
xhr_user_search_url
&&
_
.
isString
(
_converse
.
xhr_user_search_url
))
{
this
.
initXHRAutoComplete
(
this
.
el
);
this
.
el
.
addEventListener
(
'
awesomplete
-selectcomplete
'
,
ev
=>
{
this
.
initXHRAutoComplete
();
this
.
name_auto_complete
.
on
(
'
suggestion-box
-selectcomplete
'
,
ev
=>
{
this
.
el
.
querySelector
(
'
input[name="name"]
'
).
value
=
ev
.
text
.
label
;
this
.
el
.
querySelector
(
'
input[name="jid"]
'
).
value
=
ev
.
text
.
value
;
});
}
else
{
this
.
initJIDAutoComplete
(
this
.
el
);
this
.
initJIDAutoComplete
();
}
const
jid_input
=
this
.
el
.
querySelector
(
'
input[name="jid"]
'
);
this
.
el
.
addEventListener
(
'
shown.bs.modal
'
,
()
=>
jid_input
.
focus
(),
false
);
},
initJIDAutoComplete
(
root
)
{
const
jid_input
=
root
.
querySelector
(
'
input[name="jid"]
'
);
const
list
=
_
.
uniq
(
_converse
.
roster
.
map
((
item
)
=>
Strophe
.
getDomainFromJid
(
item
.
get
(
'
jid
'
))));
new
Awesomplete
(
jid_input
,
{
'
list
'
:
list
,
initJIDAutoComplete
()
{
const
el
=
this
.
el
.
querySelector
(
'
.suggestion-box__jid
'
).
parentElement
;
this
.
jid_auto_complete
=
new
_converse
.
AutoComplete
(
el
,
{
'
data
'
:
(
text
,
input
)
=>
`
${
input
.
slice
(
0
,
input
.
indexOf
(
"
@
"
))}
@
${
text
}
`
,
'
filter
'
:
Awesomplete
.
FILTER_STARTSWITH
'
filter
'
:
_converse
.
FILTER_STARTSWITH
,
'
list
'
:
_
.
uniq
(
_converse
.
roster
.
map
(
item
=>
Strophe
.
getDomainFromJid
(
item
.
get
(
'
jid
'
))))
});
},
initXHRAutoComplete
(
root
)
{
const
name_input
=
this
.
el
.
querySelector
(
'
input[name="name"]
'
)
;
const
jid_input
=
this
.
el
.
querySelector
(
'
input[name="jid"]
'
);
const
awesomplete
=
new
Awesomplete
(
name_input
,
{
'
minChars
'
:
1
,
initXHRAutoComplete
()
{
const
el
=
this
.
el
.
querySelector
(
'
.suggestion-box__name
'
).
parentElement
;
this
.
name_auto_complete
=
new
_converse
.
AutoComplete
(
el
,
{
'
auto_evaluate
'
:
false
,
'
filter
'
:
_converse
.
FILTER_STARTSWITH
,
'
list
'
:
[]
});
const
xhr
=
new
window
.
XMLHttpRequest
();
// `open` must be called after `onload` for mock/testing purposes.
xhr
.
onload
=
function
()
{
xhr
.
onload
=
()
=>
{
if
(
xhr
.
responseText
)
{
awesomplete
.
list
=
JSON
.
parse
(
xhr
.
responseText
).
map
((
i
)
=>
{
//eslint-disable-line arrow-body-style
return
{
'
label
'
:
i
.
fullname
||
i
.
jid
,
'
value
'
:
i
.
jid
}
;
})
;
awes
omplete
.
evaluate
();
const
r
=
xhr
.
responseText
;
this
.
name_auto_complete
.
list
=
JSON
.
parse
(
r
).
map
(
i
=>
({
'
label
'
:
i
.
fullname
||
i
.
jid
,
'
value
'
:
i
.
jid
}))
;
this
.
name_auto_complete
.
auto_completing
=
true
;
this
.
name_auto_c
omplete
.
evaluate
();
}
};
name_input
.
addEventListener
(
'
input
'
,
_
.
debounce
(()
=>
{
xhr
.
open
(
"
GET
"
,
`
${
_converse
.
xhr_user_search_url
}
q=
${
name_input
.
value
}
`
,
true
);
const
input_el
=
this
.
el
.
querySelector
(
'
input[name="name"]
'
);
input_el
.
addEventListener
(
'
input
'
,
_
.
debounce
(()
=>
{
xhr
.
open
(
"
GET
"
,
`
${
_converse
.
xhr_user_search_url
}
q=
${
input_el
.
value
}
`
,
true
);
xhr
.
send
()
}
,
300
));
},
...
...
@@ -183,9 +182,10 @@ converse.plugins.add('converse-rosterview', {
jid
=
data
.
get
(
'
jid
'
),
name
=
data
.
get
(
'
name
'
);
if
(
!
jid
||
_
.
compact
(
jid
.
split
(
'
@
'
)).
length
<
2
)
{
// XXX: we have to do this manually, instead of via
// XXX: we
used to
have to do this manually, instead of via
// toHTML because Awesomplete messes things up and
// confuses Snabbdom
// We now use _converse.AutoComplete, can this be removed?
u
.
addClass
(
'
is-invalid
'
,
this
.
el
.
querySelector
(
'
input[name="jid"]
'
));
u
.
addClass
(
'
d-block
'
,
this
.
el
.
querySelector
(
'
.invalid-feedback
'
));
}
else
{
...
...
src/templates/add_contact_modal.html
View file @
75ae76ad
...
...
@@ -10,16 +10,26 @@
<div
class=
"modal-body"
>
<div
class=
"form-group {[ if (o._converse.xhr_user_search_url) { ]} hidden {[ } ]}"
>
<label
class=
"clearfix"
for=
"jid"
>
{{{o.label_xmpp_address}}}:
</label>
<div
class=
"suggestion-box suggestion-box__jid"
>
<ul
class=
"suggestion-box__results suggestion-box__results--above"
hidden=
""
></ul>
<input
type=
"text"
name=
"jid"
required=
"required"
value=
"{{{o.jid}}}"
class=
"form-control
"
class=
"form-control suggestion-box__input
"
placeholder=
"{{{o.contact_placeholder}}}"
/>
<div
class=
"invalid-feedback"
>
{{{o.error_message}}}
</div>
<span
class=
"suggestion-box__additions visually-hidden"
role=
"status"
aria-live=
"assertive"
aria-relevant=
"additions"
></span>
</div>
</div>
<div
class=
"form-group"
>
<label
class=
"clearfix"
for=
"name"
>
{{{o.label_nickname}}}:
</label>
<div
class=
"suggestion-box suggestion-box__name"
>
<ul
class=
"suggestion-box__results suggestion-box__results--above"
hidden=
""
></ul>
<input
type=
"text"
name=
"name"
value=
"{{{o.nickname}}}"
class=
"form-control
"
class=
"form-control suggestion-box__input
"
placeholder=
"{{{o.nickname_placeholder}}}"
/>
<div
class=
"invalid-feedback"
>
{{{o.error_message}}}
</div>
<span
class=
"suggestion-box__additions visually-hidden"
role=
"status"
aria-live=
"assertive"
aria-relevant=
"additions"
></span>
</div>
</div>
<button
type=
"submit"
class=
"btn btn-primary"
>
{{{o.label_add}}}
</button>
</div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment