@muraai/mnl-chat 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +24 -0
  2. package/esm2020/lib/chat.module.mjs +87 -0
  3. package/esm2020/lib/components/ng-chat/ng-chat.component.mjs +588 -0
  4. package/esm2020/lib/components/ng-chat-friends-list/ng-chat-friends-list.component.mjs +128 -0
  5. package/esm2020/lib/components/ng-chat-options/ng-chat-options.component.mjs +29 -0
  6. package/esm2020/lib/components/ng-chat-window/ng-chat-window.component.mjs +247 -0
  7. package/esm2020/lib/core/chat-adapter.mjs +18 -0
  8. package/esm2020/lib/core/chat-controller.mjs +2 -0
  9. package/esm2020/lib/core/chat-group-adapter.mjs +2 -0
  10. package/esm2020/lib/core/chat-option.mjs +2 -0
  11. package/esm2020/lib/core/chat-participant-status-descriptor.mjs +6 -0
  12. package/esm2020/lib/core/chat-participant-status.enum.mjs +8 -0
  13. package/esm2020/lib/core/chat-participant-type.enum.mjs +6 -0
  14. package/esm2020/lib/core/chat-participant.mjs +2 -0
  15. package/esm2020/lib/core/default-file-upload-adapter.mjs +43 -0
  16. package/esm2020/lib/core/file-upload-adapter.mjs +2 -0
  17. package/esm2020/lib/core/group.mjs +14 -0
  18. package/esm2020/lib/core/guid.mjs +11 -0
  19. package/esm2020/lib/core/localization.mjs +2 -0
  20. package/esm2020/lib/core/message-counter.mjs +25 -0
  21. package/esm2020/lib/core/message-type.enum.mjs +7 -0
  22. package/esm2020/lib/core/message.mjs +7 -0
  23. package/esm2020/lib/core/paged-history-chat-adapter.mjs +9 -0
  24. package/esm2020/lib/core/participant-metadata.mjs +6 -0
  25. package/esm2020/lib/core/participant-response.mjs +3 -0
  26. package/esm2020/lib/core/scroll-direction.enum.mjs +6 -0
  27. package/esm2020/lib/core/theme.enum.mjs +7 -0
  28. package/esm2020/lib/core/user.mjs +7 -0
  29. package/esm2020/lib/core/window.mjs +20 -0
  30. package/esm2020/lib/firstLetter.pipe.mjs +20 -0
  31. package/esm2020/lib/material.module.mjs +50 -0
  32. package/esm2020/lib/pipes/emojify.pipe.mjs +41 -0
  33. package/esm2020/lib/pipes/group-message-display-name.pipe.mjs +24 -0
  34. package/esm2020/lib/pipes/linkfy.pipe.mjs +34 -0
  35. package/esm2020/lib/pipes/sanitize.pipe.mjs +21 -0
  36. package/esm2020/lib/services/chat.service.mjs +14 -0
  37. package/esm2020/muraai-mnl-chat.mjs +5 -0
  38. package/esm2020/public-api.mjs +25 -0
  39. package/fesm2015/muraai-mnl-chat.mjs +1426 -0
  40. package/fesm2015/muraai-mnl-chat.mjs.map +1 -0
  41. package/fesm2020/muraai-mnl-chat.mjs +1424 -0
  42. package/fesm2020/muraai-mnl-chat.mjs.map +1 -0
  43. package/index.d.ts +5 -0
  44. package/lib/chat.module.d.ts +24 -0
  45. package/lib/components/ng-chat/ng-chat.component.d.ts +130 -0
  46. package/lib/components/ng-chat-friends-list/ng-chat-friends-list.component.d.ts +45 -0
  47. package/lib/components/ng-chat-options/ng-chat-options.component.d.ts +12 -0
  48. package/lib/components/ng-chat-window/ng-chat-window.component.d.ts +65 -0
  49. package/lib/core/chat-adapter.d.ts +15 -0
  50. package/lib/core/chat-controller.d.ts +6 -0
  51. package/lib/core/chat-group-adapter.d.ts +4 -0
  52. package/lib/core/chat-option.d.ts +9 -0
  53. package/lib/core/chat-participant-status-descriptor.d.ts +3 -0
  54. package/lib/core/chat-participant-status.enum.d.ts +6 -0
  55. package/lib/core/chat-participant-type.enum.d.ts +4 -0
  56. package/lib/core/chat-participant.d.ts +9 -0
  57. package/lib/core/default-file-upload-adapter.d.ts +14 -0
  58. package/lib/core/file-upload-adapter.d.ts +5 -0
  59. package/lib/core/group.d.ts +13 -0
  60. package/lib/core/guid.d.ts +3 -0
  61. package/lib/core/localization.d.ts +15 -0
  62. package/lib/core/message-counter.d.ts +10 -0
  63. package/lib/core/message-type.enum.d.ts +5 -0
  64. package/lib/core/message.d.ts +10 -0
  65. package/lib/core/paged-history-chat-adapter.d.ts +10 -0
  66. package/lib/core/participant-metadata.d.ts +3 -0
  67. package/lib/core/participant-response.d.ts +6 -0
  68. package/lib/core/scroll-direction.enum.d.ts +4 -0
  69. package/lib/core/theme.enum.d.ts +5 -0
  70. package/lib/core/user.d.ts +10 -0
  71. package/lib/core/window.d.ts +13 -0
  72. package/lib/firstLetter.pipe.d.ts +7 -0
  73. package/lib/material.module.d.ts +14 -0
  74. package/lib/pipes/emojify.pipe.d.ts +7 -0
  75. package/lib/pipes/group-message-display-name.pipe.d.ts +9 -0
  76. package/lib/pipes/linkfy.pipe.d.ts +7 -0
  77. package/lib/pipes/sanitize.pipe.d.ts +10 -0
  78. package/lib/services/chat.service.d.ts +6 -0
  79. package/package.json +31 -0
  80. package/public-api.d.ts +21 -0
  81. package/src/assets/icons.css +140 -0
  82. package/src/assets/loading-spinner.css +59 -0
  83. package/src/assets/ng-chat.component.default.css +106 -0
  84. package/src/assets/notification.png +0 -0
  85. package/src/assets/notification.wav +0 -0
  86. package/src/assets/themes/ng-chat.theme.dark.scss +71 -0
  87. package/src/assets/themes/ng-chat.theme.default.scss +63 -0
  88. package/src/assets/user.png +0 -0
@@ -0,0 +1,1424 @@
1
+ import * as i1$2 from '@angular/common/http';
2
+ import { HttpClientModule } from '@angular/common/http';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i0 from '@angular/core';
6
+ import { NgModule, Injectable, Pipe, EventEmitter, Component, ViewEncapsulation, Input, Output, ViewChild, ViewChildren, HostListener, APP_INITIALIZER } from '@angular/core';
7
+ import { AppConfigService } from '@muraai/mnl-commons';
8
+ import { MatButtonModule } from '@angular/material/button';
9
+ import { MatIconModule } from '@angular/material/icon';
10
+ import { MatInputModule } from '@angular/material/input';
11
+ import { MatFormFieldModule } from '@angular/material/form-field';
12
+ import * as i1$1 from '@angular/forms';
13
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
14
+ import { MatMenuModule } from '@angular/material/menu';
15
+ import { map } from 'rxjs/operators';
16
+ import * as i1 from '@angular/platform-browser';
17
+
18
+ function modules() {
19
+ return [
20
+ MatButtonModule,
21
+ MatFormFieldModule,
22
+ MatIconModule,
23
+ MatInputModule,
24
+ FormsModule,
25
+ ReactiveFormsModule,
26
+ MatMenuModule,
27
+ ];
28
+ }
29
+ class ChatMaterialModule {
30
+ }
31
+ ChatMaterialModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatMaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
32
+ ChatMaterialModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: ChatMaterialModule, imports: [MatButtonModule,
33
+ MatFormFieldModule,
34
+ MatIconModule,
35
+ MatInputModule,
36
+ FormsModule,
37
+ ReactiveFormsModule,
38
+ MatMenuModule], exports: [MatButtonModule,
39
+ MatFormFieldModule,
40
+ MatIconModule,
41
+ MatInputModule,
42
+ FormsModule,
43
+ ReactiveFormsModule,
44
+ MatMenuModule] });
45
+ ChatMaterialModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatMaterialModule, imports: [modules(), MatButtonModule,
46
+ MatFormFieldModule,
47
+ MatIconModule,
48
+ MatInputModule,
49
+ FormsModule,
50
+ ReactiveFormsModule,
51
+ MatMenuModule] });
52
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatMaterialModule, decorators: [{
53
+ type: NgModule,
54
+ args: [{
55
+ imports: modules(),
56
+ exports: modules()
57
+ }]
58
+ }] });
59
+
60
+ class ChatService {
61
+ constructor() { }
62
+ }
63
+ ChatService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
64
+ ChatService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatService, providedIn: 'root' });
65
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatService, decorators: [{
66
+ type: Injectable,
67
+ args: [{
68
+ providedIn: 'root'
69
+ }]
70
+ }], ctorParameters: function () { return []; } });
71
+
72
+ var MessageType;
73
+ (function (MessageType) {
74
+ MessageType[MessageType["Text"] = 1] = "Text";
75
+ MessageType[MessageType["File"] = 2] = "File";
76
+ MessageType[MessageType["Image"] = 3] = "Image";
77
+ })(MessageType || (MessageType = {}));
78
+
79
+ class Window {
80
+ constructor(participant, isLoadingHistory, isCollapsed) {
81
+ this.messages = [];
82
+ this.newMessage = "";
83
+ // UI Behavior properties
84
+ this.isCollapsed = false;
85
+ this.isLoadingHistory = false;
86
+ this.hasFocus = false;
87
+ this.hasMoreMessages = true;
88
+ this.historyPage = 0;
89
+ this.participant = participant;
90
+ this.messages = [];
91
+ this.isLoadingHistory = isLoadingHistory;
92
+ this.hasFocus = false; // This will be triggered when the 'newMessage' input gets the current focus
93
+ this.isCollapsed = isCollapsed;
94
+ this.hasMoreMessages = false;
95
+ this.historyPage = 0;
96
+ }
97
+ }
98
+
99
+ var ChatParticipantStatus;
100
+ (function (ChatParticipantStatus) {
101
+ ChatParticipantStatus[ChatParticipantStatus["Online"] = 0] = "Online";
102
+ ChatParticipantStatus[ChatParticipantStatus["Busy"] = 1] = "Busy";
103
+ ChatParticipantStatus[ChatParticipantStatus["Away"] = 2] = "Away";
104
+ ChatParticipantStatus[ChatParticipantStatus["Offline"] = 3] = "Offline";
105
+ })(ChatParticipantStatus || (ChatParticipantStatus = {}));
106
+
107
+ var ScrollDirection;
108
+ (function (ScrollDirection) {
109
+ ScrollDirection[ScrollDirection["Top"] = 0] = "Top";
110
+ ScrollDirection[ScrollDirection["Bottom"] = 1] = "Bottom";
111
+ })(ScrollDirection || (ScrollDirection = {}));
112
+
113
+ class ChatAdapter {
114
+ constructor() {
115
+ // ### Abstract adapter methods ###
116
+ // Event handlers
117
+ /** @internal */
118
+ this.friendsListChangedHandler = (participantsResponse) => { };
119
+ /** @internal */
120
+ this.messageReceivedHandler = (participant, message) => { };
121
+ }
122
+ // ### Adapter/Chat income/ingress events ###
123
+ onFriendsListChanged(participantsResponse) {
124
+ this.friendsListChangedHandler(participantsResponse);
125
+ }
126
+ onMessageReceived(participant, message) {
127
+ this.messageReceivedHandler(participant, message);
128
+ }
129
+ }
130
+
131
+ // import { User } from "./user";
132
+ /**
133
+ * @description Chat Adapter decorator class that adds pagination to load the history of messagesr.
134
+ * You will need an existing @see ChatAdapter implementation
135
+ */
136
+ class PagedHistoryChatAdapter extends ChatAdapter {
137
+ }
138
+
139
+ /*import { IFileUploadAdapter } from './file-upload-adapter';
140
+ import { HttpClient, HttpRequest, HttpEventType, HttpResponse, HttpHeaders } from '@angular/common/http';
141
+ import { Observable, Subject } from 'rxjs';
142
+ import { User } from './user';
143
+ import { Message } from './message';*/
144
+ class DefaultFileUploadAdapter {
145
+ /**
146
+ * @summary Basic file upload adapter implementation for HTTP request form file consumption
147
+ * @param _serverEndpointUrl The API endpoint full qualified address that will receive a form file to process and return the metadata.
148
+ */
149
+ constructor(_serverEndpointUrl, _http) {
150
+ this._serverEndpointUrl = _serverEndpointUrl;
151
+ this._http = _http;
152
+ }
153
+ uploadFile(file, participantId) {
154
+ const formData = new FormData();
155
+ //formData.append('ng-chat-sender-userid', currentUserId);
156
+ formData.append('ng-chat-participant-id', participantId);
157
+ formData.append('file', file, file.name);
158
+ return this._http.post(this._serverEndpointUrl, formData);
159
+ // TODO: Leaving this if we want to track upload progress in detail in the future. Might need a different Subject generic type wrapper
160
+ // const fileRequest = new HttpRequest('POST', this._serverEndpointUrl, formData, {
161
+ // reportProgress: true
162
+ // });
163
+ // const uploadProgress = new Subject<number>();
164
+ // const uploadStatus = uploadProgress.asObservable();
165
+ //const responsePromise = new Subject<Message>();
166
+ // this._http
167
+ // .request(fileRequest)
168
+ // .subscribe(event => {
169
+ // // if (event.type == HttpEventType.UploadProgress)
170
+ // // {
171
+ // // const percentDone = Math.round(100 * event.loaded / event.total);
172
+ // // uploadProgress.next(percentDone);
173
+ // // }
174
+ // // else if (event instanceof HttpResponse)
175
+ // // {
176
+ // // uploadProgress.complete();
177
+ // // }
178
+ // });
179
+ }
180
+ }
181
+
182
+ var Theme;
183
+ (function (Theme) {
184
+ Theme["Custom"] = "custom-theme";
185
+ Theme["Light"] = "light-theme";
186
+ Theme["Dark"] = "dark-theme";
187
+ })(Theme || (Theme = {}));
188
+
189
+ // Poached from: https://github.com/Steve-Fenton/TypeScriptUtilities
190
+ // @dynamic
191
+ class Guid {
192
+ static newGuid() {
193
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
194
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
195
+ return v.toString(16);
196
+ });
197
+ }
198
+ }
199
+
200
+ var ChatParticipantType;
201
+ (function (ChatParticipantType) {
202
+ ChatParticipantType[ChatParticipantType["User"] = 0] = "User";
203
+ ChatParticipantType[ChatParticipantType["Group"] = 1] = "Group";
204
+ })(ChatParticipantType || (ChatParticipantType = {}));
205
+
206
+ class Group {
207
+ constructor(participants) {
208
+ this.id = Guid.newGuid();
209
+ this.participantType = ChatParticipantType.Group;
210
+ this.chattingTo = participants;
211
+ this.status = ChatParticipantStatus.Online;
212
+ // TODO: Add some customization for this in future releases
213
+ this.displayName = participants.map((p) => p.displayName).sort((first, second) => second > first ? -1 : 1).join(", ");
214
+ }
215
+ }
216
+
217
+ class MessageCounter {
218
+ static formatUnreadMessagesTotal(totalUnreadMessages) {
219
+ if (totalUnreadMessages > 0) {
220
+ if (totalUnreadMessages > 99)
221
+ return "99+";
222
+ else
223
+ return String(totalUnreadMessages);
224
+ }
225
+ // Empty fallback.
226
+ return "";
227
+ }
228
+ /**
229
+ * Returns a formatted string containing the total unread messages of a chat window.
230
+ * @param window The window instance to count the unread total messages.
231
+ * @param currentUserId The current chat instance user id. In this context it would be the sender.
232
+ */
233
+ static unreadMessagesTotal(window, currentUserId) {
234
+ let totalUnreadMessages = 0;
235
+ if (window) {
236
+ totalUnreadMessages = window.messages.filter(x => x.fromId != currentUserId && !x.dateSeen).length;
237
+ }
238
+ return MessageCounter.formatUnreadMessagesTotal(totalUnreadMessages);
239
+ }
240
+ }
241
+
242
+ function chatParticipantStatusDescriptor(status, localization) {
243
+ const currentStatus = ChatParticipantStatus[status].toString().toLowerCase();
244
+ return localization.statusDescription[currentStatus];
245
+ }
246
+
247
+ /*
248
+ * Sanitizes an URL resource
249
+ */
250
+ class SanitizePipe {
251
+ constructor(sanitizer) {
252
+ this.sanitizer = sanitizer;
253
+ }
254
+ transform(url) {
255
+ return this.sanitizer.bypassSecurityTrustResourceUrl(url);
256
+ }
257
+ }
258
+ SanitizePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: SanitizePipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
259
+ SanitizePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: SanitizePipe, name: "sanitize" });
260
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: SanitizePipe, decorators: [{
261
+ type: Pipe,
262
+ args: [{ name: 'sanitize' }]
263
+ }], ctorParameters: function () { return [{ type: i1.DomSanitizer }]; } });
264
+
265
+ class FirstLetterPipe {
266
+ transform(value) {
267
+ // const parts = value.split(" ");
268
+ // const firstLetter = parts[0][0];
269
+ // const secondLetters = parts.length > 1 ? parts[1].slice(0, 2) : '';
270
+ let parts = value.split(" ");
271
+ let firstLetter = parts[0].slice(0, 1);
272
+ let secondLetters = parts.length > 1 ? parts[1].slice(0, 1) : '';
273
+ return (firstLetter.toUpperCase() + secondLetters.toUpperCase());
274
+ }
275
+ }
276
+ FirstLetterPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: FirstLetterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
277
+ FirstLetterPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: FirstLetterPipe, name: "firstLetterPipe" });
278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: FirstLetterPipe, decorators: [{
279
+ type: Pipe,
280
+ args: [{ name: 'firstLetterPipe' }]
281
+ }] });
282
+
283
+ /* eslint-disable */
284
+ class NgChatFriendsListComponent {
285
+ constructor() {
286
+ this.participantsInteractedWith = [];
287
+ this.onParticipantClicked = new EventEmitter();
288
+ this.onOptionPromptCanceled = new EventEmitter();
289
+ this.onOptionPromptConfirmed = new EventEmitter();
290
+ this.hideFriendsList = false;
291
+ this.onCloseWindowClick = new EventEmitter();
292
+ this.selectedUsersFromFriendsList = [];
293
+ this.searchInput = '';
294
+ // Exposes enums and functions for the ng-template
295
+ this.ChatParticipantStatus = ChatParticipantStatus;
296
+ this.chatParticipantStatusDescriptor = chatParticipantStatusDescriptor;
297
+ this.cleanUpUserSelection = () => this.selectedUsersFromFriendsList = [];
298
+ }
299
+ ngOnChanges(changes) {
300
+ if (this.currentActiveOption) {
301
+ const currentOptionTriggeredBy = this.currentActiveOption && this.currentActiveOption.chattingTo.participant.id;
302
+ const isActivatedUserInSelectedList = (this.selectedUsersFromFriendsList.filter(item => item.id == currentOptionTriggeredBy)).length > 0;
303
+ if (!isActivatedUserInSelectedList) {
304
+ this.selectedUsersFromFriendsList = this.selectedUsersFromFriendsList.concat(this.currentActiveOption.chattingTo.participant);
305
+ }
306
+ }
307
+ // let input = user.displayName;
308
+ // let parts = input.split(" ");
309
+ // let firstLetter = parts[0];
310
+ // let secondLetters = parts[1].slice(0, 2);
311
+ }
312
+ get filteredParticipants() {
313
+ if (this.searchInput.length > 0) {
314
+ // Searches in the friend list by the inputted search string
315
+ return this.participants.filter(x => x.displayName.toUpperCase().includes(this.searchInput.toUpperCase()));
316
+ }
317
+ console.log("this.participants", this.participants);
318
+ return this.participants;
319
+ }
320
+ isUserSelectedFromFriendsList(user) {
321
+ return (this.selectedUsersFromFriendsList.filter(item => item.id == user.id)).length > 0;
322
+ }
323
+ unreadMessagesTotalByParticipant(participant) {
324
+ let openedWindow = this.windows.find(x => x.participant.id == participant.id);
325
+ if (openedWindow) {
326
+ return MessageCounter.unreadMessagesTotal(openedWindow, this.userId);
327
+ }
328
+ else {
329
+ let totalUnreadMessages = this.participantsResponse
330
+ .filter(x => x.participant.id == participant.id && !this.participantsInteractedWith.find(u => u.id == participant.id) && x.metadata && x.metadata.totalUnreadMessages > 0)
331
+ .map((participantResponse) => {
332
+ return participantResponse.metadata.totalUnreadMessages;
333
+ })[0];
334
+ return MessageCounter.formatUnreadMessagesTotal(totalUnreadMessages);
335
+ }
336
+ }
337
+ // Toggle friends list visibility
338
+ onChatTitleClicked() {
339
+ this.isCollapsed = !this.isCollapsed;
340
+ }
341
+ onFriendsListCheckboxChange(selectedUser, isChecked) {
342
+ if (isChecked) {
343
+ this.selectedUsersFromFriendsList.push(selectedUser);
344
+ }
345
+ else {
346
+ this.selectedUsersFromFriendsList.splice(this.selectedUsersFromFriendsList.indexOf(selectedUser), 1);
347
+ }
348
+ }
349
+ onUserClick(clickedUser) {
350
+ this.onParticipantClicked.emit(clickedUser);
351
+ }
352
+ onFriendsListActionCancelClicked() {
353
+ this.onOptionPromptCanceled.emit();
354
+ this.cleanUpUserSelection();
355
+ }
356
+ onFriendsListActionConfirmClicked() {
357
+ this.onOptionPromptConfirmed.emit(this.selectedUsersFromFriendsList);
358
+ this.cleanUpUserSelection();
359
+ }
360
+ onCloseWindow() {
361
+ this.onCloseWindowClick.emit(true);
362
+ console.log('hoi latest friendlist', this.shouldDisplay);
363
+ }
364
+ }
365
+ NgChatFriendsListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChatFriendsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
366
+ NgChatFriendsListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NgChatFriendsListComponent, selector: "ng-chat-friends-list", inputs: { participants: "participants", participantsResponse: "participantsResponse", participantsInteractedWith: "participantsInteractedWith", windows: "windows", userId: "userId", localization: "localization", shouldDisplay: "shouldDisplay", isCollapsed: "isCollapsed", searchEnabled: "searchEnabled", currentActiveOption: "currentActiveOption", hideFriendsList: "hideFriendsList" }, outputs: { onParticipantClicked: "onParticipantClicked", onOptionPromptCanceled: "onOptionPromptCanceled", onOptionPromptConfirmed: "onOptionPromptConfirmed", onCloseWindowClick: "onCloseWindowClick" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"shouldDisplay\" id=\"ng-chat-people\" [ngClass]=\"{'primary-outline-color': true, 'primary-background': true, 'ng-chat-people-collapsed': isCollapsed}\">\n\t<a href=\"javascript:void(0);\" class=\"ng-chat-title secondary-background shadowed\" (click)=\"onChatTitleClicked()\">\n\t\t<span>\n\t\t\t{{localization.title}}\n\t\t</span>\n\t<a href=\"javascript:void(0);\" class=\" ng-chat-close \" (click)=\"onCloseWindow()\" >&#x2715;</a>\n\t</a>\n\t<div *ngIf=\"currentActiveOption\" class=\"ng-chat-people-actions\" (click)=\"onFriendsListActionCancelClicked()\">\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\">\n\t\t\t<i class=\"remove-icon\"></i>\n\t\t</a>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\" (click)=\"onFriendsListActionConfirmClicked()\">\n\t\t\t<i class=\"check-icon\"></i>\n\t\t</a>\n\t</div>\n\t<input *ngIf=\"searchEnabled\" id=\"ng-chat-search_friend\" class=\"friends-search-bar\" type=\"search\" [placeholder]=\"localization.searchPlaceholder\" [(ngModel)]=\"searchInput\" />\n\t<ul id=\"ng-chat-users\" *ngIf=\"!isCollapsed\" [ngClass]=\"{'offset-search': searchEnabled}\">\n\t\t<li *ngFor=\"let user of filteredParticipants\">\n\t\t\t<input \n\t\t\t\t*ngIf=\"currentActiveOption && currentActiveOption.validateContext(user)\" \n\t\t\t\ttype=\"checkbox\" \n\t\t\t\tclass=\"ng-chat-users-checkbox\" \n\t\t\t\t(change)=\"onFriendsListCheckboxChange(user, $event.target.checked)\" \n\t\t\t\t[checked]=\"isUserSelectedFromFriendsList(user)\"/>\n\t\t\t<div [ngClass]=\"{'ng-chat-friends-list-selectable-offset': currentActiveOption, 'ng-chat-friends-list-container': true}\" (click)=\"onUserClick(user)\">\n\t\t\t\t<!-- <div *ngIf=\"!user.avatar\" class=\"icon-wrapper\">\n\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t</div> -->\n\t\t\t\t<div class=\"user\" *ngIf=\"!user.avatar\">\n\t\t\t\t\t{{user.displayName|firstLetterPipe}}\n\n\t\t\t\t</div>\n\n\t\t\t\t<img *ngIf=\"user.avatar\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"user.avatar | sanitize\"/>\n\t\t\t\t<div class=\"\"></div>\n\t\t\t\t<strong title=\"{{user.displayName}}\">{{user.displayName}}</strong>\n\t\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': user.status === ChatParticipantStatus.Online, 'busy': user.status === ChatParticipantStatus.Busy, 'away': user.status === ChatParticipantStatus.Away, 'offline': user.status === ChatParticipantStatus.Offline}\" title=\"{{chatParticipantStatusDescriptor(user.status, localization)}}\"></span>\n\t\t\t\t<span *ngIf=\"unreadMessagesTotalByParticipant(user).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotalByParticipant(user)}}</span>\n\t\t\t</div>\n\t\t</li>\n\t</ul>\n</div>", styles: ["#ng-chat-people{position:relative;width:240px;height:360px;border-width:1px;border-style:solid;margin-right:20px;box-shadow:0 4px 8px #00000040;border-bottom:0;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;margin-bottom:10px!important;border:1px solid grey!important}#ng-chat-people.ng-chat-people-collapsed{height:30px}#ng-chat-search_friend{display:block;padding:7px 10px;margin:10px auto 0;width:calc(100% - 20px);font-size:.9em;border-radius:20px!important;border:.5px solid grey!important}#ng-chat-users{padding:0 10px;list-style:none;margin:0;overflow:auto;position:absolute;top:42px;bottom:0;width:100%;box-sizing:border-box}#ng-chat-users.offset-search{top:84px}#ng-chat-users .ng-chat-users-checkbox{float:left;margin-right:5px;margin-top:8px}#ng-chat-users li{clear:both;margin-bottom:10px;overflow:hidden;cursor:pointer;max-height:30px}#ng-chat-users li>.ng-chat-friends-list-selectable-offset{margin-left:22px}#ng-chat-users li .ng-chat-friends-list-container{display:inline-block;width:100%}#ng-chat-users li>.ng-chat-friends-list-selectable-offset.ng-chat-friends-list-container{display:block;width:auto}#ng-chat-users li .ng-chat-friends-list-container>img.avatar,#ng-chat-users li .ng-chat-friends-list-container>.user{float:left;display:flex;justify-content:center;margin-right:5px}#ng-chat-users li .ng-chat-friends-list-container>.user{overflow:hidden;width:30px;height:30px}#ng-chat-users li .ng-chat-friends-list-container>.user>i{color:#fff;transform:scale(.7)}#ng-chat-users li .ng-chat-friends-list-container>strong{float:left;line-height:30px;font-size:.8em;max-width:57%;max-height:30px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#ng-chat-users li .ng-chat-friends-list-container>.ng-chat-participant-status{float:right}.ng-chat-people-actions{position:absolute;top:4px;right:5px;margin:0;padding:0;z-index:2}.ng-chat-people-actions>a.ng-chat-people-action{display:inline-block;width:21px;height:21px;margin-right:8px;text-decoration:none;border:none;border-radius:25px;padding:1px}@media only screen and (max-width: 581px){#ng-chat-people{width:300px;height:360px;margin-right:0}}.user{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background-color:#00b04c;color:#fff;font-size:12px;font-family:inherit}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: SanitizePipe, name: "sanitize" }, { kind: "pipe", type: FirstLetterPipe, name: "firstLetterPipe" }], encapsulation: i0.ViewEncapsulation.None });
367
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChatFriendsListComponent, decorators: [{
368
+ type: Component,
369
+ args: [{ selector: 'ng-chat-friends-list', encapsulation: ViewEncapsulation.None, template: "<div *ngIf=\"shouldDisplay\" id=\"ng-chat-people\" [ngClass]=\"{'primary-outline-color': true, 'primary-background': true, 'ng-chat-people-collapsed': isCollapsed}\">\n\t<a href=\"javascript:void(0);\" class=\"ng-chat-title secondary-background shadowed\" (click)=\"onChatTitleClicked()\">\n\t\t<span>\n\t\t\t{{localization.title}}\n\t\t</span>\n\t<a href=\"javascript:void(0);\" class=\" ng-chat-close \" (click)=\"onCloseWindow()\" >&#x2715;</a>\n\t</a>\n\t<div *ngIf=\"currentActiveOption\" class=\"ng-chat-people-actions\" (click)=\"onFriendsListActionCancelClicked()\">\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\">\n\t\t\t<i class=\"remove-icon\"></i>\n\t\t</a>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\" (click)=\"onFriendsListActionConfirmClicked()\">\n\t\t\t<i class=\"check-icon\"></i>\n\t\t</a>\n\t</div>\n\t<input *ngIf=\"searchEnabled\" id=\"ng-chat-search_friend\" class=\"friends-search-bar\" type=\"search\" [placeholder]=\"localization.searchPlaceholder\" [(ngModel)]=\"searchInput\" />\n\t<ul id=\"ng-chat-users\" *ngIf=\"!isCollapsed\" [ngClass]=\"{'offset-search': searchEnabled}\">\n\t\t<li *ngFor=\"let user of filteredParticipants\">\n\t\t\t<input \n\t\t\t\t*ngIf=\"currentActiveOption && currentActiveOption.validateContext(user)\" \n\t\t\t\ttype=\"checkbox\" \n\t\t\t\tclass=\"ng-chat-users-checkbox\" \n\t\t\t\t(change)=\"onFriendsListCheckboxChange(user, $event.target.checked)\" \n\t\t\t\t[checked]=\"isUserSelectedFromFriendsList(user)\"/>\n\t\t\t<div [ngClass]=\"{'ng-chat-friends-list-selectable-offset': currentActiveOption, 'ng-chat-friends-list-container': true}\" (click)=\"onUserClick(user)\">\n\t\t\t\t<!-- <div *ngIf=\"!user.avatar\" class=\"icon-wrapper\">\n\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t</div> -->\n\t\t\t\t<div class=\"user\" *ngIf=\"!user.avatar\">\n\t\t\t\t\t{{user.displayName|firstLetterPipe}}\n\n\t\t\t\t</div>\n\n\t\t\t\t<img *ngIf=\"user.avatar\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"user.avatar | sanitize\"/>\n\t\t\t\t<div class=\"\"></div>\n\t\t\t\t<strong title=\"{{user.displayName}}\">{{user.displayName}}</strong>\n\t\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': user.status === ChatParticipantStatus.Online, 'busy': user.status === ChatParticipantStatus.Busy, 'away': user.status === ChatParticipantStatus.Away, 'offline': user.status === ChatParticipantStatus.Offline}\" title=\"{{chatParticipantStatusDescriptor(user.status, localization)}}\"></span>\n\t\t\t\t<span *ngIf=\"unreadMessagesTotalByParticipant(user).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotalByParticipant(user)}}</span>\n\t\t\t</div>\n\t\t</li>\n\t</ul>\n</div>", styles: ["#ng-chat-people{position:relative;width:240px;height:360px;border-width:1px;border-style:solid;margin-right:20px;box-shadow:0 4px 8px #00000040;border-bottom:0;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;margin-bottom:10px!important;border:1px solid grey!important}#ng-chat-people.ng-chat-people-collapsed{height:30px}#ng-chat-search_friend{display:block;padding:7px 10px;margin:10px auto 0;width:calc(100% - 20px);font-size:.9em;border-radius:20px!important;border:.5px solid grey!important}#ng-chat-users{padding:0 10px;list-style:none;margin:0;overflow:auto;position:absolute;top:42px;bottom:0;width:100%;box-sizing:border-box}#ng-chat-users.offset-search{top:84px}#ng-chat-users .ng-chat-users-checkbox{float:left;margin-right:5px;margin-top:8px}#ng-chat-users li{clear:both;margin-bottom:10px;overflow:hidden;cursor:pointer;max-height:30px}#ng-chat-users li>.ng-chat-friends-list-selectable-offset{margin-left:22px}#ng-chat-users li .ng-chat-friends-list-container{display:inline-block;width:100%}#ng-chat-users li>.ng-chat-friends-list-selectable-offset.ng-chat-friends-list-container{display:block;width:auto}#ng-chat-users li .ng-chat-friends-list-container>img.avatar,#ng-chat-users li .ng-chat-friends-list-container>.user{float:left;display:flex;justify-content:center;margin-right:5px}#ng-chat-users li .ng-chat-friends-list-container>.user{overflow:hidden;width:30px;height:30px}#ng-chat-users li .ng-chat-friends-list-container>.user>i{color:#fff;transform:scale(.7)}#ng-chat-users li .ng-chat-friends-list-container>strong{float:left;line-height:30px;font-size:.8em;max-width:57%;max-height:30px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#ng-chat-users li .ng-chat-friends-list-container>.ng-chat-participant-status{float:right}.ng-chat-people-actions{position:absolute;top:4px;right:5px;margin:0;padding:0;z-index:2}.ng-chat-people-actions>a.ng-chat-people-action{display:inline-block;width:21px;height:21px;margin-right:8px;text-decoration:none;border:none;border-radius:25px;padding:1px}@media only screen and (max-width: 581px){#ng-chat-people{width:300px;height:360px;margin-right:0}}.user{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background-color:#00b04c;color:#fff;font-size:12px;font-family:inherit}\n"] }]
370
+ }], ctorParameters: function () { return []; }, propDecorators: { participants: [{
371
+ type: Input
372
+ }], participantsResponse: [{
373
+ type: Input
374
+ }], participantsInteractedWith: [{
375
+ type: Input
376
+ }], windows: [{
377
+ type: Input
378
+ }], userId: [{
379
+ type: Input
380
+ }], localization: [{
381
+ type: Input
382
+ }], shouldDisplay: [{
383
+ type: Input
384
+ }], isCollapsed: [{
385
+ type: Input
386
+ }], searchEnabled: [{
387
+ type: Input
388
+ }], currentActiveOption: [{
389
+ type: Input
390
+ }], onParticipantClicked: [{
391
+ type: Output
392
+ }], onOptionPromptCanceled: [{
393
+ type: Output
394
+ }], onOptionPromptConfirmed: [{
395
+ type: Output
396
+ }], hideFriendsList: [{
397
+ type: Input
398
+ }], onCloseWindowClick: [{
399
+ type: Output
400
+ }] } });
401
+
402
+ class Message {
403
+ constructor() {
404
+ this.type = MessageType.Text;
405
+ }
406
+ }
407
+
408
+ let emojiDictionary = [
409
+ { patterns: [':)', ':-)', '=)'], unicode: '😃' },
410
+ { patterns: [':D', ':-D', '=D'], unicode: '😀' },
411
+ { patterns: [':(', ':-(', '=('], unicode: '🙁' },
412
+ { patterns: [':|', ':-|', '=|'], unicode: '😐' },
413
+ { patterns: [':*', ':-*', '=*'], unicode: '😙' },
414
+ { patterns: ['T_T', 'T.T'], unicode: '😭' },
415
+ { patterns: [':O', ':-O', '=O', ':o', ':-o', '=o'], unicode: '😮' },
416
+ { patterns: [':P', ':-P', '=P', ':p', ':-p', '=p'], unicode: '😋' },
417
+ { patterns: ['>.<'], unicode: '😣' },
418
+ { patterns: ['@.@'], unicode: '😵' },
419
+ { patterns: ['*.*'], unicode: '😍' },
420
+ { patterns: ['<3'], unicode: '❤️' },
421
+ { patterns: ['^.^'], unicode: '😊' },
422
+ { patterns: [':+1'], unicode: '👍' },
423
+ { patterns: [':-1'], unicode: '👎' }
424
+ ];
425
+ /*
426
+ * Transforms common emoji text to UTF encoded emojis
427
+ */
428
+ class EmojifyPipe {
429
+ transform(message, pipeEnabled) {
430
+ if (pipeEnabled && message && message.length > 1) {
431
+ emojiDictionary.forEach(emoji => {
432
+ emoji.patterns.forEach(pattern => {
433
+ message = message.replace(pattern, emoji.unicode);
434
+ });
435
+ });
436
+ }
437
+ return message;
438
+ }
439
+ }
440
+ EmojifyPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: EmojifyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
441
+ EmojifyPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: EmojifyPipe, name: "emojify" });
442
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: EmojifyPipe, decorators: [{
443
+ type: Pipe,
444
+ args: [{ name: 'emojify' }]
445
+ }] });
446
+
447
+ /*
448
+ * Transforms text containing URLs or E-mails to valid links/mailtos
449
+ */
450
+ class LinkfyPipe {
451
+ transform(message, pipeEnabled) {
452
+ if (pipeEnabled && message && message.length > 1) {
453
+ let replacedText;
454
+ let replacePatternProtocol;
455
+ let replacePatternWWW;
456
+ let replacePatternMailTo;
457
+ // URLs starting with http://, https://, or ftp://
458
+ replacePatternProtocol = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
459
+ replacedText = message.replace(replacePatternProtocol, '<a href="$1" target="_blank">$1</a>');
460
+ // URLs starting with "www." (ignoring // before it).
461
+ replacePatternWWW = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
462
+ replacedText = replacedText.replace(replacePatternWWW, '$1<a href="http://$2" target="_blank">$2</a>');
463
+ // Change email addresses to mailto:: links.
464
+ replacePatternMailTo = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
465
+ replacedText = replacedText.replace(replacePatternMailTo, '<a href="mailto:$1">$1</a>');
466
+ return replacedText;
467
+ }
468
+ else
469
+ return message;
470
+ }
471
+ }
472
+ LinkfyPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LinkfyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
473
+ LinkfyPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: LinkfyPipe, name: "linkfy" });
474
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LinkfyPipe, decorators: [{
475
+ type: Pipe,
476
+ args: [{ name: 'linkfy' }]
477
+ }] });
478
+
479
+ /*
480
+ * Renders the display name of a participant in a group based on who's sent the message
481
+ */
482
+ class GroupMessageDisplayNamePipe {
483
+ transform(participant, message) {
484
+ if (participant && participant.participantType == ChatParticipantType.Group) {
485
+ let group = participant;
486
+ let userIndex = group.chattingTo.findIndex(x => x.id == message.fromId);
487
+ return group.chattingTo[userIndex >= 0 ? userIndex : 0].displayName;
488
+ }
489
+ else
490
+ return "";
491
+ }
492
+ }
493
+ GroupMessageDisplayNamePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GroupMessageDisplayNamePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
494
+ GroupMessageDisplayNamePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: GroupMessageDisplayNamePipe, name: "groupMessageDisplayName" });
495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GroupMessageDisplayNamePipe, decorators: [{
496
+ type: Pipe,
497
+ args: [{ name: 'groupMessageDisplayName' }]
498
+ }] });
499
+
500
+ /* eslint-disable */
501
+ class NgChatWindowComponent {
502
+ constructor() {
503
+ this.emojisEnabled = true;
504
+ this.linkfyEnabled = true;
505
+ this.showMessageDate = true;
506
+ this.messageDatePipeFormat = "short";
507
+ this.hasPagedHistory = true;
508
+ this.onChatWindowClosed = new EventEmitter();
509
+ this.onMessagesSeen = new EventEmitter();
510
+ this.onMessageSent = new EventEmitter();
511
+ this.onTabTriggered = new EventEmitter();
512
+ this.onOptionTriggered = new EventEmitter();
513
+ this.onLoadHistoryTriggered = new EventEmitter();
514
+ // File upload state
515
+ this.fileUploadersInUse = []; // Id bucket of uploaders in use
516
+ // Exposes enums and functions for the ng-template
517
+ this.ChatParticipantType = ChatParticipantType;
518
+ this.ChatParticipantStatus = ChatParticipantStatus;
519
+ this.MessageType = MessageType;
520
+ this.chatParticipantStatusDescriptor = chatParticipantStatusDescriptor;
521
+ }
522
+ defaultWindowOptions(currentWindow) {
523
+ if (this.showOptions && currentWindow.participant.participantType == ChatParticipantType.User) {
524
+ return [{
525
+ isActive: false,
526
+ chattingTo: currentWindow,
527
+ validateContext: (participant) => {
528
+ return participant.participantType == ChatParticipantType.User;
529
+ },
530
+ displayLabel: 'Add People' // TODO: Localize this
531
+ }];
532
+ }
533
+ return [];
534
+ }
535
+ // Asserts if a user avatar is visible in a chat cluster
536
+ isAvatarVisible(window, message, index) {
537
+ if (message.fromId != this.userId) {
538
+ if (index == 0) {
539
+ return true; // First message, good to show the thumbnail
540
+ }
541
+ else {
542
+ // Check if the previous message belongs to the same user, if it belongs there is no need to show the avatar again to form the message cluster
543
+ if (window.messages[index - 1].fromId != message.fromId) {
544
+ return true;
545
+ }
546
+ }
547
+ }
548
+ return false;
549
+ }
550
+ getChatWindowAvatar(participant, message) {
551
+ if (participant.participantType == ChatParticipantType.User) {
552
+ return participant.avatar;
553
+ }
554
+ else if (participant.participantType == ChatParticipantType.Group) {
555
+ let group = participant;
556
+ let userIndex = group.chattingTo.findIndex(x => x.id == message.fromId);
557
+ return group.chattingTo[userIndex >= 0 ? userIndex : 0].avatar;
558
+ }
559
+ return null;
560
+ }
561
+ isUploadingFile(window) {
562
+ const fileUploadInstanceId = this.getUniqueFileUploadInstanceId(window);
563
+ return this.fileUploadersInUse.indexOf(fileUploadInstanceId) > -1;
564
+ }
565
+ // Generates a unique file uploader id for each participant
566
+ getUniqueFileUploadInstanceId(window) {
567
+ if (window && window.participant) {
568
+ return `ng-chat-file-upload-${window.participant.id}`;
569
+ }
570
+ return 'ng-chat-file-upload';
571
+ }
572
+ unreadMessagesTotal(window) {
573
+ return MessageCounter.unreadMessagesTotal(window, this.userId);
574
+ }
575
+ // Scrolls a chat window message flow to the bottom
576
+ scrollChatWindow(window, direction) {
577
+ if (!window.isCollapsed) {
578
+ setTimeout(() => {
579
+ if (this.chatMessages) {
580
+ let element = this.chatMessages.nativeElement;
581
+ let position = (direction === ScrollDirection.Top) ? 0 : element.scrollHeight;
582
+ element.scrollTop = position;
583
+ }
584
+ });
585
+ }
586
+ }
587
+ activeOptionTrackerChange(option) {
588
+ this.onOptionTriggered.emit(option);
589
+ }
590
+ // Triggers native file upload for file selection from the user
591
+ triggerNativeFileUpload(window) {
592
+ if (window) {
593
+ if (this.nativeFileInput)
594
+ this.nativeFileInput.nativeElement.click();
595
+ }
596
+ }
597
+ // Toggles a window focus on the focus/blur of a 'newMessage' input
598
+ toggleWindowFocus(window) {
599
+ window.hasFocus = !window.hasFocus;
600
+ if (window.hasFocus) {
601
+ const unreadMessages = window.messages
602
+ .filter(message => message.dateSeen == null
603
+ && (message.toId == this.userId || window.participant.participantType === ChatParticipantType.Group));
604
+ if (unreadMessages && unreadMessages.length > 0) {
605
+ this.onMessagesSeen.emit(unreadMessages);
606
+ }
607
+ }
608
+ }
609
+ markMessagesAsRead(messages) {
610
+ this.onMessagesSeen.emit(messages);
611
+ }
612
+ fetchMessageHistory(window) {
613
+ this.onLoadHistoryTriggered.emit(window);
614
+ }
615
+ // Closes a chat window via the close 'X' button
616
+ onCloseChatWindow() {
617
+ this.onChatWindowClosed.emit({ closedWindow: this.window, closedViaEscapeKey: false });
618
+ }
619
+ /* Monitors pressed keys on a chat window
620
+ - Dispatches a message when the ENTER key is pressed
621
+ - Tabs between windows on TAB or SHIFT + TAB
622
+ - Closes the current focused window on ESC
623
+ */
624
+ onChatInputTyped(event, window) {
625
+ switch (event.keyCode) {
626
+ case 13:
627
+ if (window.newMessage && window.newMessage.trim() != "") {
628
+ let message = new Message();
629
+ message.fromId = this.userId;
630
+ message.toId = window.participant.id;
631
+ message.message = window.newMessage;
632
+ message.dateSent = new Date();
633
+ window.messages.push(message);
634
+ this.onMessageSent.emit(message);
635
+ window.newMessage = ""; // Resets the new message input
636
+ this.scrollChatWindow(window, ScrollDirection.Bottom);
637
+ }
638
+ break;
639
+ case 9:
640
+ event.preventDefault();
641
+ this.onTabTriggered.emit({ triggeringWindow: window, shiftKeyPressed: event.shiftKey });
642
+ break;
643
+ case 27:
644
+ this.onChatWindowClosed.emit({ closedWindow: window, closedViaEscapeKey: true });
645
+ break;
646
+ }
647
+ }
648
+ // Toggles a chat window visibility between maximized/minimized
649
+ onChatWindowClicked(window) {
650
+ window.isCollapsed = !window.isCollapsed;
651
+ this.scrollChatWindow(window, ScrollDirection.Bottom);
652
+ }
653
+ clearInUseFileUploader(fileUploadInstanceId) {
654
+ const uploaderInstanceIdIndex = this.fileUploadersInUse.indexOf(fileUploadInstanceId);
655
+ if (uploaderInstanceIdIndex > -1) {
656
+ this.fileUploadersInUse.splice(uploaderInstanceIdIndex, 1);
657
+ }
658
+ }
659
+ // Handles file selection and uploads the selected file using the file upload adapter
660
+ onFileChosen(window) {
661
+ const fileUploadInstanceId = this.getUniqueFileUploadInstanceId(window);
662
+ const uploadElementRef = this.nativeFileInput;
663
+ if (uploadElementRef) {
664
+ const file = uploadElementRef.nativeElement.files[0];
665
+ this.fileUploadersInUse.push(fileUploadInstanceId);
666
+ this.fileUploadAdapter.uploadFile(file, window.participant.id)
667
+ .subscribe(fileMessage => {
668
+ this.clearInUseFileUploader(fileUploadInstanceId);
669
+ fileMessage.fromId = this.userId;
670
+ // Push file message to current user window
671
+ window.messages.push(fileMessage);
672
+ this.onMessageSent.emit(fileMessage);
673
+ this.scrollChatWindow(window, ScrollDirection.Bottom);
674
+ // Resets the file upload element
675
+ uploadElementRef.nativeElement.value = '';
676
+ }, (error) => {
677
+ this.clearInUseFileUploader(fileUploadInstanceId);
678
+ // Resets the file upload element
679
+ uploadElementRef.nativeElement.value = '';
680
+ // TODO: Invoke a file upload adapter error here
681
+ });
682
+ }
683
+ }
684
+ }
685
+ NgChatWindowComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChatWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
686
+ NgChatWindowComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NgChatWindowComponent, selector: "ng-chat-window", inputs: { fileUploadAdapter: "fileUploadAdapter", window: "window", userId: "userId", localization: "localization", showOptions: "showOptions", emojisEnabled: "emojisEnabled", linkfyEnabled: "linkfyEnabled", showMessageDate: "showMessageDate", messageDatePipeFormat: "messageDatePipeFormat", hasPagedHistory: "hasPagedHistory" }, outputs: { onChatWindowClosed: "onChatWindowClosed", onMessagesSeen: "onMessagesSeen", onMessageSent: "onMessageSent", onTabTriggered: "onTabTriggered", onOptionTriggered: "onOptionTriggered", onLoadHistoryTriggered: "onLoadHistoryTriggered" }, viewQueries: [{ propertyName: "chatMessages", first: true, predicate: ["chatMessages"], descendants: true }, { propertyName: "nativeFileInput", first: true, predicate: ["nativeFileInput"], descendants: true }, { propertyName: "chatWindowInput", first: true, predicate: ["chatWindowInput"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"window && window.isCollapsed\">\n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong title=\"{{window.participant.displayName}}\">\n\t\t\t\t{{window.participant.displayName}}\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" title=\"{{chatParticipantStatusDescriptor(window.participant.status, localization)}}\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t</div>\n</ng-container>\n<ng-container *ngIf=\"window && !window.isCollapsed\"> \n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong title=\"{{window.participant.displayName}}\">\n\t\t\t\t{{window.participant.displayName}}\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" title=\"{{chatParticipantStatusDescriptor(window.participant.status, localization)}}\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t\t<!-- <ng-chat-options [ngClass]=\"'ng-chat-options-container'\" [options]=\"defaultWindowOptions(window)\" (activeOptionTrackerChange)=\"activeOptionTrackerChange($event)\"></ng-chat-options> -->\n\t</div>\n\t\t<div #chatMessages class=\"ng-chat-messages primary-background\">\n\t\t\t<div *ngIf=\"window.isLoadingHistory\" class=\"ng-chat-loading-wrapper\">\n\t\t\t\t<div class=\"loader\">Loading history...</div>\n\t\t\t</div>\n\t\t\t<div *ngIf=\"hasPagedHistory && window.hasMoreMessages && !window.isLoadingHistory\" class=\"ng-chat-load-history\">\n\t\t\t\t<a class=\"load-history-action\" (click)=\"fetchMessageHistory(window)\">{{localization.loadMessageHistoryPlaceholder}}</a>\n\t\t\t</div>\n\n\t\t\t<div *ngFor=\"let message of window.messages; let i = index\" [ngClass]=\"{'ng-chat-message': true, 'ng-chat-message-received': message.fromId !== userId}\">\n\t\t\t\t<ng-container *ngIf=\"isAvatarVisible(window, message, i)\">\n\t\t\t\t\t<div *ngIf=\"!getChatWindowAvatar(window.participant, message)\" class=\"icon-wrapper\">\n\t\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- <img *ngIf=\"getChatWindowAvatar(window.participant, message)\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"getChatWindowAvatar(window.participant, message) | sanitize\" /> -->\n\t\t\t\t\t<span *ngIf=\"window.participant.participantType === ChatParticipantType.Group\" class=\"ng-chat-participant-name\">{{window.participant | groupMessageDisplayName:message}}</span>\n\t\t\t\t</ng-container>\n\t\t\t\t<ng-container [ngSwitch]=\"message.type\">\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Text\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\t\t \t\t\t<span [innerHtml]=\"message.message | emojify:emojisEnabled | linkfy:linkfyEnabled\"></span>\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Image\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\n\t\t\t \t\t\t<img src=\"{{message.message}}\" class=\"image-message\" />\n\t\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.File\" [ngClass]=\"{'file-message-container': true, 'received': message.fromId !== userId}\">\n\t\t\t\t\t\t<div class=\"file-message-icon-container\">\n\t\t\t\t\t\t\t<i class=\"paperclip-icon\"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<!-- <a class=\"file-details\" [attr.href]=\"message.downloadUrl\" target=\"_blank\" rel=\"noopener noreferrer\" (click)=\"this.markMessagesAsRead([message])\" download>\n\t\t\t\t\t\t\t<span class=\"file-message-title\" [attr.title]=\"message.message\">{{message.message}}</span>\n\t\t\t\t\t\t\t<span *ngIf=\"message.fileSizeInBytes\" class=\"file-message-size\">{{message.fileSizeInBytes}} Bytes</span>\n\t\t\t\t\t\t</a> -->\n\t\t\t\t\t</div>\n\t\t\t\t</ng-container>\n\t\t\t</div>\n\t</div>\n\n\t<div class=\"ng-chat-footer primary-outline-color primary-background\">\n\t\t<input #chatWindowInput\n\t\t\ttype=\"text\"\n\t\t\t[ngModel]=\"window.newMessage | emojify:emojisEnabled\"\n\t\t\t(ngModelChange)=\"window.newMessage=$event\"\n\t\t\t[placeholder]=\"localization.messagePlaceholder\"\n\t\t\t[ngClass]=\"{'chat-window-input': true, 'has-side-action': fileUploadAdapter}\"\n\t\t\t(keydown)=\"onChatInputTyped($event, window)\"\n\t\t\t(blur)=\"toggleWindowFocus(window)\"\n\t\t\t(focus)=\"toggleWindowFocus(window)\"/>\n\n\t\t<!-- File Upload -->\n\t\t<ng-container *ngIf=\"fileUploadAdapter\">\n\t\t\t<a *ngIf=\"!isUploadingFile(window)\" class=\"btn-add-file\" (click)=\"triggerNativeFileUpload(window)\">\n\t\t\t\t<i class=\"upload-icon\"></i>\n\t\t\t</a>\n\t\t\t<input\n\t\t\t\ttype=\"file\"\n\t\t\t\t#nativeFileInput\n\t\t\t\tstyle=\"display: none;\"\n\t\t\t\t[attr.id]=\"getUniqueFileUploadInstanceId(window)\"\n\t\t\t\t(change)=\"onFileChosen(window)\" />\n\t\t\t<div *ngIf=\"isUploadingFile(window)\" class=\"loader\"></div>\n\t\t</ng-container>\n\t</div>\n</ng-container>", styles: [".ng-chat-window{right:260px;height:360px;z-index:999;bottom:0;width:300px;position:fixed;border-width:1px;border-style:solid;border-bottom:0;box-shadow:0 4px 8px #00000040;margin-bottom:10px!important;border:1px solid grey!important;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;background-color:#fff;::-webkit-scrollbar {width: 8px !important;} ::-webkit-scrollbar-thumb {background: #1b1b70 !important; border-radius: 5px !important;}}.ng-chat-window-collapsed{height:30px!important}.ng-chat-window .ng-chat-footer{box-sizing:border-box!important;padding-bottom:10px!important;display:block;height:10%;width:95%!important;margin:auto!important;background-color:inherit!important}.ng-chat-window .ng-chat-footer>input{font-size:.8em;box-sizing:border-box!important;padding:15px!important;height:100%;width:100%;border-radius:20px!important;border:1px solid grey!important}.ng-chat-window .ng-chat-footer>input.has-side-action{width:calc(100% - 30px)}.ng-chat-window .ng-chat-footer .btn-add-file{position:absolute;right:5px;bottom:7px;height:20px;width:20px;cursor:pointer}.ng-chat-window .ng-chat-footer .loader{position:absolute;right:14px;bottom:8px}.ng-chat-window .ng-chat-load-history{height:30px;text-align:center;font-size:.8em}.ng-chat-window .ng-chat-load-history>a{border-radius:15px;cursor:pointer;padding:5px 10px}.ng-chat-window .ng-chat-messages{padding:10px;width:100%;height:calc(90% - 30px);box-sizing:border-box;position:relative;overflow:auto}.ng-chat-window .ng-chat-messages .ng-chat-message{clear:both}.ng-chat-window .ng-chat-messages .ng-chat-message>img.avatar,.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{position:absolute;left:10px;border-radius:25px}.ng-chat-window .ng-chat-messages .ng-chat-message .ng-chat-participant-name{display:inline-block;margin-left:40px;padding-bottom:5px;font-weight:700;font-size:.8em;text-overflow:ellipsis;max-width:180px}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{background-color:#bababa;overflow:hidden;width:30px;height:30px;padding:0}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper>i{color:#fff;transform:scale(.7)}.ng-chat-window .ng-chat-messages .ng-chat-message .message-sent-date{font-size:.5em!important;display:block;text-align:right;margin-top:5px;color:#a1a0a0!important;font-style:italic!important}.ng-chat-window .ng-chat-messages .ng-chat-message>div{float:right;width:182px;border-radius:15px 15px 0!important;padding:10px;margin-top:0;margin-bottom:5px;font-size:.9em;word-wrap:break-word;background-color:#93d4f0!important}.ng-chat-window .ng-chat-message-received>div.received-chat-message-container{float:left;margin-left:40px;padding-top:7px;padding-bottom:7px;margin-top:0;margin-bottom:5px;background-color:#daf2fc!important;border-radius:15px 15px 15px 0!important}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container{float:right;width:202px;border-style:solid;border-width:3px;border-radius:5px;overflow:hidden;margin-bottom:5px;display:block;text-decoration:none;font-size:.9em;padding:0;box-sizing:border-box}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container.received{float:left;margin-left:40px;width:208px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container{width:20px;height:35px;padding:10px 5px;float:left}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container i{margin-top:8px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details{float:left;padding:10px;width:calc(100% - 60px);color:currentColor;text-decoration:none}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details:hover{text-decoration:underline}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details span{display:block;width:100%;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-title{font-weight:700}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-size{font-size:.8em;margin-top:5px}.ng-chat-window .image-message{width:100%;height:auto}@media only screen and (max-width: 581px){.ng-chat-window{position:initial}}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }, { kind: "pipe", type: EmojifyPipe, name: "emojify" }, { kind: "pipe", type: LinkfyPipe, name: "linkfy" }, { kind: "pipe", type: GroupMessageDisplayNamePipe, name: "groupMessageDisplayName" }], encapsulation: i0.ViewEncapsulation.None });
687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChatWindowComponent, decorators: [{
688
+ type: Component,
689
+ args: [{ selector: 'ng-chat-window', encapsulation: ViewEncapsulation.None, template: "<ng-container *ngIf=\"window && window.isCollapsed\">\n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong title=\"{{window.participant.displayName}}\">\n\t\t\t\t{{window.participant.displayName}}\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" title=\"{{chatParticipantStatusDescriptor(window.participant.status, localization)}}\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t</div>\n</ng-container>\n<ng-container *ngIf=\"window && !window.isCollapsed\"> \n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong title=\"{{window.participant.displayName}}\">\n\t\t\t\t{{window.participant.displayName}}\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" title=\"{{chatParticipantStatusDescriptor(window.participant.status, localization)}}\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t\t<!-- <ng-chat-options [ngClass]=\"'ng-chat-options-container'\" [options]=\"defaultWindowOptions(window)\" (activeOptionTrackerChange)=\"activeOptionTrackerChange($event)\"></ng-chat-options> -->\n\t</div>\n\t\t<div #chatMessages class=\"ng-chat-messages primary-background\">\n\t\t\t<div *ngIf=\"window.isLoadingHistory\" class=\"ng-chat-loading-wrapper\">\n\t\t\t\t<div class=\"loader\">Loading history...</div>\n\t\t\t</div>\n\t\t\t<div *ngIf=\"hasPagedHistory && window.hasMoreMessages && !window.isLoadingHistory\" class=\"ng-chat-load-history\">\n\t\t\t\t<a class=\"load-history-action\" (click)=\"fetchMessageHistory(window)\">{{localization.loadMessageHistoryPlaceholder}}</a>\n\t\t\t</div>\n\n\t\t\t<div *ngFor=\"let message of window.messages; let i = index\" [ngClass]=\"{'ng-chat-message': true, 'ng-chat-message-received': message.fromId !== userId}\">\n\t\t\t\t<ng-container *ngIf=\"isAvatarVisible(window, message, i)\">\n\t\t\t\t\t<div *ngIf=\"!getChatWindowAvatar(window.participant, message)\" class=\"icon-wrapper\">\n\t\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- <img *ngIf=\"getChatWindowAvatar(window.participant, message)\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"getChatWindowAvatar(window.participant, message) | sanitize\" /> -->\n\t\t\t\t\t<span *ngIf=\"window.participant.participantType === ChatParticipantType.Group\" class=\"ng-chat-participant-name\">{{window.participant | groupMessageDisplayName:message}}</span>\n\t\t\t\t</ng-container>\n\t\t\t\t<ng-container [ngSwitch]=\"message.type\">\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Text\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\t\t \t\t\t<span [innerHtml]=\"message.message | emojify:emojisEnabled | linkfy:linkfyEnabled\"></span>\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Image\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\n\t\t\t \t\t\t<img src=\"{{message.message}}\" class=\"image-message\" />\n\t\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.File\" [ngClass]=\"{'file-message-container': true, 'received': message.fromId !== userId}\">\n\t\t\t\t\t\t<div class=\"file-message-icon-container\">\n\t\t\t\t\t\t\t<i class=\"paperclip-icon\"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<!-- <a class=\"file-details\" [attr.href]=\"message.downloadUrl\" target=\"_blank\" rel=\"noopener noreferrer\" (click)=\"this.markMessagesAsRead([message])\" download>\n\t\t\t\t\t\t\t<span class=\"file-message-title\" [attr.title]=\"message.message\">{{message.message}}</span>\n\t\t\t\t\t\t\t<span *ngIf=\"message.fileSizeInBytes\" class=\"file-message-size\">{{message.fileSizeInBytes}} Bytes</span>\n\t\t\t\t\t\t</a> -->\n\t\t\t\t\t</div>\n\t\t\t\t</ng-container>\n\t\t\t</div>\n\t</div>\n\n\t<div class=\"ng-chat-footer primary-outline-color primary-background\">\n\t\t<input #chatWindowInput\n\t\t\ttype=\"text\"\n\t\t\t[ngModel]=\"window.newMessage | emojify:emojisEnabled\"\n\t\t\t(ngModelChange)=\"window.newMessage=$event\"\n\t\t\t[placeholder]=\"localization.messagePlaceholder\"\n\t\t\t[ngClass]=\"{'chat-window-input': true, 'has-side-action': fileUploadAdapter}\"\n\t\t\t(keydown)=\"onChatInputTyped($event, window)\"\n\t\t\t(blur)=\"toggleWindowFocus(window)\"\n\t\t\t(focus)=\"toggleWindowFocus(window)\"/>\n\n\t\t<!-- File Upload -->\n\t\t<ng-container *ngIf=\"fileUploadAdapter\">\n\t\t\t<a *ngIf=\"!isUploadingFile(window)\" class=\"btn-add-file\" (click)=\"triggerNativeFileUpload(window)\">\n\t\t\t\t<i class=\"upload-icon\"></i>\n\t\t\t</a>\n\t\t\t<input\n\t\t\t\ttype=\"file\"\n\t\t\t\t#nativeFileInput\n\t\t\t\tstyle=\"display: none;\"\n\t\t\t\t[attr.id]=\"getUniqueFileUploadInstanceId(window)\"\n\t\t\t\t(change)=\"onFileChosen(window)\" />\n\t\t\t<div *ngIf=\"isUploadingFile(window)\" class=\"loader\"></div>\n\t\t</ng-container>\n\t</div>\n</ng-container>", styles: [".ng-chat-window{right:260px;height:360px;z-index:999;bottom:0;width:300px;position:fixed;border-width:1px;border-style:solid;border-bottom:0;box-shadow:0 4px 8px #00000040;margin-bottom:10px!important;border:1px solid grey!important;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;background-color:#fff;::-webkit-scrollbar {width: 8px !important;} ::-webkit-scrollbar-thumb {background: #1b1b70 !important; border-radius: 5px !important;}}.ng-chat-window-collapsed{height:30px!important}.ng-chat-window .ng-chat-footer{box-sizing:border-box!important;padding-bottom:10px!important;display:block;height:10%;width:95%!important;margin:auto!important;background-color:inherit!important}.ng-chat-window .ng-chat-footer>input{font-size:.8em;box-sizing:border-box!important;padding:15px!important;height:100%;width:100%;border-radius:20px!important;border:1px solid grey!important}.ng-chat-window .ng-chat-footer>input.has-side-action{width:calc(100% - 30px)}.ng-chat-window .ng-chat-footer .btn-add-file{position:absolute;right:5px;bottom:7px;height:20px;width:20px;cursor:pointer}.ng-chat-window .ng-chat-footer .loader{position:absolute;right:14px;bottom:8px}.ng-chat-window .ng-chat-load-history{height:30px;text-align:center;font-size:.8em}.ng-chat-window .ng-chat-load-history>a{border-radius:15px;cursor:pointer;padding:5px 10px}.ng-chat-window .ng-chat-messages{padding:10px;width:100%;height:calc(90% - 30px);box-sizing:border-box;position:relative;overflow:auto}.ng-chat-window .ng-chat-messages .ng-chat-message{clear:both}.ng-chat-window .ng-chat-messages .ng-chat-message>img.avatar,.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{position:absolute;left:10px;border-radius:25px}.ng-chat-window .ng-chat-messages .ng-chat-message .ng-chat-participant-name{display:inline-block;margin-left:40px;padding-bottom:5px;font-weight:700;font-size:.8em;text-overflow:ellipsis;max-width:180px}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{background-color:#bababa;overflow:hidden;width:30px;height:30px;padding:0}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper>i{color:#fff;transform:scale(.7)}.ng-chat-window .ng-chat-messages .ng-chat-message .message-sent-date{font-size:.5em!important;display:block;text-align:right;margin-top:5px;color:#a1a0a0!important;font-style:italic!important}.ng-chat-window .ng-chat-messages .ng-chat-message>div{float:right;width:182px;border-radius:15px 15px 0!important;padding:10px;margin-top:0;margin-bottom:5px;font-size:.9em;word-wrap:break-word;background-color:#93d4f0!important}.ng-chat-window .ng-chat-message-received>div.received-chat-message-container{float:left;margin-left:40px;padding-top:7px;padding-bottom:7px;margin-top:0;margin-bottom:5px;background-color:#daf2fc!important;border-radius:15px 15px 15px 0!important}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container{float:right;width:202px;border-style:solid;border-width:3px;border-radius:5px;overflow:hidden;margin-bottom:5px;display:block;text-decoration:none;font-size:.9em;padding:0;box-sizing:border-box}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container.received{float:left;margin-left:40px;width:208px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container{width:20px;height:35px;padding:10px 5px;float:left}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container i{margin-top:8px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details{float:left;padding:10px;width:calc(100% - 60px);color:currentColor;text-decoration:none}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details:hover{text-decoration:underline}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details span{display:block;width:100%;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-title{font-weight:700}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-size{font-size:.8em;margin-top:5px}.ng-chat-window .image-message{width:100%;height:auto}@media only screen and (max-width: 581px){.ng-chat-window{position:initial}}\n"] }]
690
+ }], ctorParameters: function () { return []; }, propDecorators: { fileUploadAdapter: [{
691
+ type: Input
692
+ }], window: [{
693
+ type: Input
694
+ }], userId: [{
695
+ type: Input
696
+ }], localization: [{
697
+ type: Input
698
+ }], showOptions: [{
699
+ type: Input
700
+ }], emojisEnabled: [{
701
+ type: Input
702
+ }], linkfyEnabled: [{
703
+ type: Input
704
+ }], showMessageDate: [{
705
+ type: Input
706
+ }], messageDatePipeFormat: [{
707
+ type: Input
708
+ }], hasPagedHistory: [{
709
+ type: Input
710
+ }], onChatWindowClosed: [{
711
+ type: Output
712
+ }], onMessagesSeen: [{
713
+ type: Output
714
+ }], onMessageSent: [{
715
+ type: Output
716
+ }], onTabTriggered: [{
717
+ type: Output
718
+ }], onOptionTriggered: [{
719
+ type: Output
720
+ }], onLoadHistoryTriggered: [{
721
+ type: Output
722
+ }], chatMessages: [{
723
+ type: ViewChild,
724
+ args: ['chatMessages']
725
+ }], nativeFileInput: [{
726
+ type: ViewChild,
727
+ args: ['nativeFileInput']
728
+ }], chatWindowInput: [{
729
+ type: ViewChild,
730
+ args: ['chatWindowInput']
731
+ }] } });
732
+
733
+ /* eslint-disable */
734
+ class NgChat {
735
+ constructor(_httpClient) {
736
+ this._httpClient = _httpClient;
737
+ // Exposes enums for the ng-template
738
+ this.ChatParticipantType = ChatParticipantType;
739
+ this.ChatParticipantStatus = ChatParticipantStatus;
740
+ this.MessageType = MessageType;
741
+ this._isDisabled = false;
742
+ this.isCollapsed = false;
743
+ this.maximizeWindowOnNewMessage = true;
744
+ this.pollFriendsList = false;
745
+ this.pollingInterval = 5000;
746
+ this.historyEnabled = true;
747
+ this.emojisEnabled = true;
748
+ this.linkfyEnabled = true;
749
+ this.audioEnabled = true;
750
+ this.searchEnabled = true;
751
+ this.audioSource = '../../../assets/notification.wav';
752
+ this.persistWindowsState = true;
753
+ this.title = "Quick chat";
754
+ this.messagePlaceholder = "Type a message";
755
+ this.searchPlaceholder = "Search";
756
+ this.browserNotificationsEnabled = true;
757
+ this.browserNotificationIconSource = '../../../assets/notification.png';
758
+ this.browserNotificationTitle = "New message from";
759
+ this.historyPageSize = 10;
760
+ this.hideFriendsList = false;
761
+ this.hideFriendsListOnUnsupportedViewport = true;
762
+ this.theme = Theme.Light;
763
+ this.messageDatePipeFormat = "short";
764
+ this.showMessageDate = true;
765
+ this.isViewportOnMobileEnabled = false;
766
+ this.onParticipantClicked = new EventEmitter();
767
+ this.onParticipantChatOpened = new EventEmitter();
768
+ this.onParticipantChatClosed = new EventEmitter();
769
+ this.onMessagesSeen = new EventEmitter();
770
+ this.onCloseWindowClicked = new EventEmitter();
771
+ this.browserNotificationsBootstrapped = false;
772
+ this.hasPagedHistory = false;
773
+ // Don't want to add this as a setting to simplify usage. Previous placeholder and title settings available to be used, or use full Localization object.
774
+ this.statusDescription = {
775
+ online: 'Online',
776
+ busy: 'Busy',
777
+ away: 'Away',
778
+ offline: 'Offline'
779
+ };
780
+ this.participantsInteractedWith = [];
781
+ // Defines the size of each opened window to calculate how many windows can be opened on the viewport at the same time.
782
+ this.windowSizeFactor = 320;
783
+ // Total width size of the friends list section
784
+ this.friendsListWidth = 262;
785
+ // Set to true if there is no space to display at least one chat window and 'hideFriendsListOnUnsupportedViewport' is true
786
+ this.unsupportedViewport = false;
787
+ this.windows = [];
788
+ this.isBootstrapped = false;
789
+ }
790
+ get isDisabled() {
791
+ return this._isDisabled;
792
+ }
793
+ set isDisabled(value) {
794
+ this._isDisabled = value;
795
+ if (value) {
796
+ window.clearInterval(this.pollingIntervalWindowInstance);
797
+ }
798
+ else {
799
+ this.activateFriendListFetch();
800
+ }
801
+ }
802
+ get localStorageKey() {
803
+ return `ng-chat-users-${this.userId}`; // Appending the user id so the state is unique per user in a computer.
804
+ }
805
+ ;
806
+ ngOnInit() {
807
+ this.bootstrapChat();
808
+ }
809
+ onResize(event) {
810
+ this.viewPortTotalArea = event.target.innerWidth;
811
+ this.NormalizeWindows();
812
+ }
813
+ // Checks if there are more opened windows than the view port can display
814
+ NormalizeWindows() {
815
+ const maxSupportedOpenedWindows = Math.floor((this.viewPortTotalArea - (!this.hideFriendsList ? this.friendsListWidth : 0)) / this.windowSizeFactor);
816
+ const difference = this.windows.length - maxSupportedOpenedWindows;
817
+ if (difference >= 0) {
818
+ this.windows.splice(this.windows.length - difference);
819
+ }
820
+ this.updateWindowsState(this.windows);
821
+ // Viewport should have space for at least one chat window but should show in mobile if option is enabled.
822
+ this.unsupportedViewport = this.isViewportOnMobileEnabled ? false : this.hideFriendsListOnUnsupportedViewport && maxSupportedOpenedWindows < 1;
823
+ }
824
+ // Initializes the chat plugin and the messaging adapter
825
+ bootstrapChat() {
826
+ let initializationException = null;
827
+ if (this.adapter != null && this.userId != null) {
828
+ try {
829
+ this.viewPortTotalArea = window.innerWidth;
830
+ this.initializeTheme();
831
+ this.initializeDefaultText();
832
+ this.initializeBrowserNotifications();
833
+ // Binding event listeners
834
+ this.adapter.messageReceivedHandler = (participant, msg) => this.onMessageReceived(participant, msg);
835
+ this.adapter.friendsListChangedHandler = (participantsResponse) => this.onFriendsListChanged(participantsResponse);
836
+ this.activateFriendListFetch();
837
+ this.bufferAudioFile();
838
+ this.hasPagedHistory = this.adapter instanceof PagedHistoryChatAdapter;
839
+ if (this.fileUploadUrl && this.fileUploadUrl !== "") {
840
+ this.fileUploadAdapter = new DefaultFileUploadAdapter(this.fileUploadUrl, this._httpClient);
841
+ }
842
+ this.NormalizeWindows();
843
+ this.isBootstrapped = true;
844
+ }
845
+ catch (ex) {
846
+ initializationException = ex;
847
+ }
848
+ }
849
+ if (!this.isBootstrapped) {
850
+ console.error("ng-chat component couldn't be bootstrapped.");
851
+ if (this.userId == null) {
852
+ console.error("ng-chat can't be initialized without an user id. Please make sure you've provided an userId as a parameter of the ng-chat component.");
853
+ }
854
+ if (this.adapter == null) {
855
+ console.error("ng-chat can't be bootstrapped without a ChatAdapter. Please make sure you've provided a ChatAdapter implementation as a parameter of the ng-chat component.");
856
+ }
857
+ if (initializationException) {
858
+ console.error(`An exception has occurred while initializing ng-chat. Details: ${initializationException.message}`);
859
+ console.error(initializationException);
860
+ }
861
+ }
862
+ }
863
+ activateFriendListFetch() {
864
+ if (this.adapter) {
865
+ // Loading current users list
866
+ if (this.pollFriendsList) {
867
+ // Setting a long poll interval to update the friends list
868
+ this.fetchFriendsList(true);
869
+ this.pollingIntervalWindowInstance = window.setInterval(() => this.fetchFriendsList(false), this.pollingInterval);
870
+ }
871
+ else {
872
+ // Since polling was disabled, a friends list update mechanism will have to be implemented in the ChatAdapter.
873
+ this.fetchFriendsList(true);
874
+ }
875
+ }
876
+ }
877
+ // Initializes browser notifications
878
+ async initializeBrowserNotifications() {
879
+ if (this.browserNotificationsEnabled && ("Notification" in window)) {
880
+ if (await Notification.requestPermission() === "granted") {
881
+ this.browserNotificationsBootstrapped = true;
882
+ }
883
+ }
884
+ }
885
+ // Initializes default text
886
+ initializeDefaultText() {
887
+ if (!this.localization) {
888
+ this.localization = {
889
+ messagePlaceholder: this.messagePlaceholder,
890
+ searchPlaceholder: this.searchPlaceholder,
891
+ title: this.title,
892
+ statusDescription: this.statusDescription,
893
+ browserNotificationTitle: this.browserNotificationTitle,
894
+ loadMessageHistoryPlaceholder: "Load older messages"
895
+ };
896
+ }
897
+ }
898
+ initializeTheme() {
899
+ if (this.customTheme) {
900
+ this.theme = Theme.Custom;
901
+ }
902
+ else if (this.theme != Theme.Light && this.theme != Theme.Dark) {
903
+ // TODO: Use es2017 in future with Object.values(Theme).includes(this.theme) to do this check
904
+ throw new Error(`Invalid theme configuration for ng-chat. "${this.theme}" is not a valid theme value.`);
905
+ }
906
+ }
907
+ // Sends a request to load the friends list
908
+ fetchFriendsList(isBootstrapping) {
909
+ this.adapter.listFriends()
910
+ .pipe(map((participantsResponse) => {
911
+ this.participantsResponse = participantsResponse;
912
+ this.participants = participantsResponse.map((response) => {
913
+ return response.participant;
914
+ });
915
+ })).subscribe(() => {
916
+ if (isBootstrapping) {
917
+ this.restoreWindowsState();
918
+ }
919
+ });
920
+ }
921
+ fetchMessageHistory(window) {
922
+ // Not ideal but will keep this until we decide if we are shipping pagination with the default adapter
923
+ if (this.adapter instanceof PagedHistoryChatAdapter) {
924
+ window.isLoadingHistory = true;
925
+ this.adapter.getMessageHistoryByPage(window.participant.id, this.historyPageSize, ++window.historyPage)
926
+ .pipe(map((result) => {
927
+ result.forEach((message) => this.assertMessageType(message));
928
+ window.messages = result.concat(window.messages);
929
+ window.isLoadingHistory = false;
930
+ const direction = (window.historyPage == 1) ? ScrollDirection.Bottom : ScrollDirection.Top;
931
+ window.hasMoreMessages = result.length == this.historyPageSize;
932
+ setTimeout(() => this.onFetchMessageHistoryLoaded(result, window, direction, true));
933
+ })).subscribe();
934
+ }
935
+ else {
936
+ this.adapter.getMessageHistory(window.participant.id)
937
+ .pipe(map((result) => {
938
+ result.forEach((message) => this.assertMessageType(message));
939
+ window.messages = result.concat(window.messages);
940
+ window.isLoadingHistory = false;
941
+ setTimeout(() => this.onFetchMessageHistoryLoaded(result, window, ScrollDirection.Bottom));
942
+ })).subscribe();
943
+ }
944
+ }
945
+ onFetchMessageHistoryLoaded(messages, window, direction, forceMarkMessagesAsSeen = false) {
946
+ this.scrollChatWindow(window, direction);
947
+ if (window.hasFocus || forceMarkMessagesAsSeen) {
948
+ const unseenMessages = messages.filter(m => !m.dateSeen);
949
+ this.markMessagesAsRead(unseenMessages);
950
+ }
951
+ }
952
+ // Updates the friends list via the event handler
953
+ onFriendsListChanged(participantsResponse) {
954
+ if (participantsResponse) {
955
+ this.participantsResponse = participantsResponse;
956
+ this.participants = participantsResponse.map((response) => {
957
+ return response.participant;
958
+ });
959
+ this.participantsInteractedWith = [];
960
+ }
961
+ }
962
+ // Handles received messages by the adapter
963
+ onMessageReceived(participant, message) {
964
+ if (participant && message) {
965
+ const chatWindow = this.openChatWindow(participant);
966
+ this.assertMessageType(message);
967
+ if (!chatWindow[1] || !this.historyEnabled) {
968
+ chatWindow[0].messages.push(message);
969
+ this.scrollChatWindow(chatWindow[0], ScrollDirection.Bottom);
970
+ if (chatWindow[0].hasFocus) {
971
+ this.markMessagesAsRead([message]);
972
+ }
973
+ }
974
+ this.emitMessageSound(chatWindow[0]);
975
+ // Do not push browser notifications with message content for privacy purposes if the 'maximizeWindowOnNewMessage' setting is off and this is a new chat window.
976
+ if (this.maximizeWindowOnNewMessage || (!chatWindow[1] && !chatWindow[0].isCollapsed)) {
977
+ // Some messages are not pushed because they are loaded by fetching the history hence why we supply the message here
978
+ this.emitBrowserNotification(chatWindow[0], message);
979
+ }
980
+ }
981
+ }
982
+ onParticipantClickedFromFriendsList(participant) {
983
+ this.openChatWindow(participant, true, true);
984
+ }
985
+ cancelOptionPrompt() {
986
+ if (this.currentActiveOption) {
987
+ this.currentActiveOption.isActive = false;
988
+ this.currentActiveOption = null;
989
+ }
990
+ }
991
+ onOptionPromptCanceled() {
992
+ this.cancelOptionPrompt();
993
+ }
994
+ onOptionPromptConfirmed(event) {
995
+ // For now this is fine as there is only one option available. Introduce option types and type checking if a new option is added.
996
+ this.confirmNewGroup(event);
997
+ // Canceling current state
998
+ this.cancelOptionPrompt();
999
+ }
1000
+ confirmNewGroup(users) {
1001
+ const newGroup = new Group(users);
1002
+ this.openChatWindow(newGroup);
1003
+ if (this.groupAdapter) {
1004
+ this.groupAdapter.groupCreated(newGroup);
1005
+ }
1006
+ }
1007
+ // Opens a new chat whindow. Takes care of available viewport
1008
+ // Works for opening a chat window for an user or for a group
1009
+ // Returns => [Window: Window object reference, boolean: Indicates if this window is a new chat window]
1010
+ openChatWindow(participant, focusOnNewWindow = false, invokedByUserClick = false) {
1011
+ // Is this window opened?
1012
+ const openedWindow = this.windows.find(x => x.participant.id == participant.id);
1013
+ if (!openedWindow) {
1014
+ if (invokedByUserClick) {
1015
+ this.onParticipantClicked.emit(participant);
1016
+ }
1017
+ // Refer to issue #58 on Github
1018
+ const collapseWindow = invokedByUserClick ? false : !this.maximizeWindowOnNewMessage;
1019
+ const newChatWindow = new Window(participant, this.historyEnabled, collapseWindow);
1020
+ // Loads the chat history via an RxJs Observable
1021
+ if (this.historyEnabled) {
1022
+ this.fetchMessageHistory(newChatWindow);
1023
+ }
1024
+ this.windows.unshift(newChatWindow);
1025
+ // Is there enough space left in the view port ? but should be displayed in mobile if option is enabled
1026
+ if (!this.isViewportOnMobileEnabled) {
1027
+ if (this.windows.length * this.windowSizeFactor >= this.viewPortTotalArea - (!this.hideFriendsList ? this.friendsListWidth : 0)) {
1028
+ this.windows.pop();
1029
+ }
1030
+ }
1031
+ this.updateWindowsState(this.windows);
1032
+ if (focusOnNewWindow && !collapseWindow) {
1033
+ this.focusOnWindow(newChatWindow);
1034
+ }
1035
+ this.participantsInteractedWith.push(participant);
1036
+ this.onParticipantChatOpened.emit(participant);
1037
+ return [newChatWindow, true];
1038
+ }
1039
+ else {
1040
+ // Returns the existing chat window
1041
+ return [openedWindow, false];
1042
+ }
1043
+ }
1044
+ // Focus on the input element of the supplied window
1045
+ focusOnWindow(window, callback = () => { }) {
1046
+ const windowIndex = this.windows.indexOf(window);
1047
+ if (windowIndex >= 0) {
1048
+ setTimeout(() => {
1049
+ if (this.chatWindows) {
1050
+ const chatWindowToFocus = this.chatWindows.toArray()[windowIndex];
1051
+ chatWindowToFocus.chatWindowInput.nativeElement.focus();
1052
+ }
1053
+ callback();
1054
+ });
1055
+ }
1056
+ }
1057
+ assertMessageType(message) {
1058
+ // Always fallback to "Text" messages to avoid rendenring issues
1059
+ if (!message.type) {
1060
+ message.type = MessageType.Text;
1061
+ }
1062
+ }
1063
+ // Marks all messages provided as read with the current time.
1064
+ markMessagesAsRead(messages) {
1065
+ const currentDate = new Date();
1066
+ messages.forEach((msg) => {
1067
+ msg.dateSeen = currentDate;
1068
+ });
1069
+ this.onMessagesSeen.emit(messages);
1070
+ }
1071
+ // Buffers audio file (For component's bootstrapping)
1072
+ bufferAudioFile() {
1073
+ if (this.audioSource && this.audioSource.length > 0) {
1074
+ this.audioFile = new Audio();
1075
+ this.audioFile.src = this.audioSource;
1076
+ this.audioFile.load();
1077
+ }
1078
+ }
1079
+ // Emits a message notification audio if enabled after every message received
1080
+ emitMessageSound(window) {
1081
+ if (this.audioEnabled && !window.hasFocus && this.audioFile) {
1082
+ this.audioFile.play();
1083
+ }
1084
+ }
1085
+ // Emits a browser notification
1086
+ emitBrowserNotification(window, message) {
1087
+ // if (this.browserNotificationsBootstrapped && !window.hasFocus && message) {
1088
+ if (this.browserNotificationsBootstrapped && message) {
1089
+ const notification = new Notification(`${this.localization.browserNotificationTitle} ${window.participant.displayName}`, {
1090
+ 'body': message.message,
1091
+ 'icon': this.browserNotificationIconSource
1092
+ });
1093
+ setTimeout(() => {
1094
+ notification.close();
1095
+ }, message.message.length <= 50 ? 5000 : 7000); // More time to read longer messages
1096
+ }
1097
+ }
1098
+ // Saves current windows state into local storage if persistence is enabled
1099
+ updateWindowsState(windows) {
1100
+ if (this.persistWindowsState) {
1101
+ const participantIds = windows.map((w) => {
1102
+ return w.participant.id;
1103
+ });
1104
+ localStorage.setItem(this.localStorageKey, JSON.stringify(participantIds));
1105
+ }
1106
+ }
1107
+ restoreWindowsState() {
1108
+ try {
1109
+ if (this.persistWindowsState) {
1110
+ const stringfiedParticipantIds = localStorage.getItem(this.localStorageKey);
1111
+ if (stringfiedParticipantIds && stringfiedParticipantIds.length > 0) {
1112
+ const participantIds = JSON.parse(stringfiedParticipantIds);
1113
+ const participantsToRestore = this.participants.filter(u => participantIds.indexOf(u.id) >= 0);
1114
+ participantsToRestore.forEach((participant) => {
1115
+ this.openChatWindow(participant);
1116
+ });
1117
+ }
1118
+ }
1119
+ }
1120
+ catch (ex) {
1121
+ console.error(`An error occurred while restoring ng-chat windows state. Details: ${ex}`);
1122
+ }
1123
+ }
1124
+ // Gets closest open window if any. Most recent opened has priority (Right)
1125
+ getClosestWindow(window) {
1126
+ const index = this.windows.indexOf(window);
1127
+ if (index > 0) {
1128
+ return this.windows[index - 1];
1129
+ }
1130
+ else if (index == 0 && this.windows.length > 1) {
1131
+ return this.windows[index + 1];
1132
+ }
1133
+ }
1134
+ closeWindow(window) {
1135
+ const index = this.windows.indexOf(window);
1136
+ this.windows.splice(index, 1);
1137
+ this.updateWindowsState(this.windows);
1138
+ this.onParticipantChatClosed.emit(window.participant);
1139
+ }
1140
+ getChatWindowComponentInstance(targetWindow) {
1141
+ const windowIndex = this.windows.indexOf(targetWindow);
1142
+ if (this.chatWindows) {
1143
+ let targetWindow = this.chatWindows.toArray()[windowIndex];
1144
+ return targetWindow;
1145
+ }
1146
+ return null;
1147
+ }
1148
+ // Scrolls a chat window message flow to the bottom
1149
+ scrollChatWindow(window, direction) {
1150
+ const chatWindow = this.getChatWindowComponentInstance(window);
1151
+ if (chatWindow) {
1152
+ chatWindow.scrollChatWindow(window, direction);
1153
+ }
1154
+ }
1155
+ onWindowMessagesSeen(messagesSeen) {
1156
+ this.markMessagesAsRead(messagesSeen);
1157
+ }
1158
+ onWindowChatClosed(payload) {
1159
+ const { closedWindow, closedViaEscapeKey } = payload;
1160
+ if (closedViaEscapeKey) {
1161
+ let closestWindow = this.getClosestWindow(closedWindow);
1162
+ if (closestWindow) {
1163
+ this.focusOnWindow(closestWindow, () => { this.closeWindow(closedWindow); });
1164
+ }
1165
+ else {
1166
+ this.closeWindow(closedWindow);
1167
+ }
1168
+ }
1169
+ else {
1170
+ this.closeWindow(closedWindow);
1171
+ }
1172
+ }
1173
+ onWindowTabTriggered(payload) {
1174
+ const { triggeringWindow, shiftKeyPressed } = payload;
1175
+ const currentWindowIndex = this.windows.indexOf(triggeringWindow);
1176
+ let windowToFocus = this.windows[currentWindowIndex + (shiftKeyPressed ? 1 : -1)]; // Goes back on shift + tab
1177
+ if (!windowToFocus) {
1178
+ // Edge windows, go to start or end
1179
+ windowToFocus = this.windows[currentWindowIndex > 0 ? 0 : this.chatWindows.length - 1];
1180
+ }
1181
+ this.focusOnWindow(windowToFocus);
1182
+ }
1183
+ onWindowMessageSent(messageSent) {
1184
+ this.adapter.sendMessage(messageSent);
1185
+ }
1186
+ onWindowOptionTriggered(option) {
1187
+ this.currentActiveOption = option;
1188
+ }
1189
+ triggerOpenChatWindow(user) {
1190
+ if (user) {
1191
+ this.openChatWindow(user);
1192
+ }
1193
+ }
1194
+ triggerCloseChatWindow(userId) {
1195
+ const openedWindow = this.windows.find(x => x.participant.id == userId);
1196
+ if (openedWindow) {
1197
+ this.closeWindow(openedWindow);
1198
+ }
1199
+ }
1200
+ triggerToggleChatWindowVisibility(userId) {
1201
+ const openedWindow = this.windows.find(x => x.participant.id == userId);
1202
+ if (openedWindow) {
1203
+ const chatWindow = this.getChatWindowComponentInstance(openedWindow);
1204
+ if (chatWindow) {
1205
+ chatWindow.onChatWindowClicked(openedWindow);
1206
+ }
1207
+ }
1208
+ }
1209
+ // closeFriendList(){
1210
+ // this.hideFriendsList = !this.hideFriendsList
1211
+ // this.hideFriendsListChange.emit(this.hideFriendsList)
1212
+ // }
1213
+ onCloseWindowClick(click) {
1214
+ this.onCloseWindowClicked.emit(click);
1215
+ }
1216
+ }
1217
+ NgChat.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChat, deps: [{ token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
1218
+ NgChat.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NgChat, selector: "mnl-chat", inputs: { isDisabled: "isDisabled", adapter: "adapter", groupAdapter: "groupAdapter", userId: "userId", isCollapsed: "isCollapsed", maximizeWindowOnNewMessage: "maximizeWindowOnNewMessage", pollFriendsList: "pollFriendsList", pollingInterval: "pollingInterval", historyEnabled: "historyEnabled", emojisEnabled: "emojisEnabled", linkfyEnabled: "linkfyEnabled", audioEnabled: "audioEnabled", searchEnabled: "searchEnabled", audioSource: "audioSource", persistWindowsState: "persistWindowsState", title: "title", messagePlaceholder: "messagePlaceholder", searchPlaceholder: "searchPlaceholder", browserNotificationsEnabled: "browserNotificationsEnabled", browserNotificationIconSource: "browserNotificationIconSource", browserNotificationTitle: "browserNotificationTitle", historyPageSize: "historyPageSize", localization: "localization", hideFriendsList: "hideFriendsList", hideFriendsListOnUnsupportedViewport: "hideFriendsListOnUnsupportedViewport", fileUploadUrl: "fileUploadUrl", theme: "theme", customTheme: "customTheme", messageDatePipeFormat: "messageDatePipeFormat", showMessageDate: "showMessageDate", isViewportOnMobileEnabled: "isViewportOnMobileEnabled", fileUploadAdapter: "fileUploadAdapter" }, outputs: { onParticipantClicked: "onParticipantClicked", onParticipantChatOpened: "onParticipantChatOpened", onParticipantChatClosed: "onParticipantChatClosed", onMessagesSeen: "onMessagesSeen", onCloseWindowClicked: "onCloseWindowClicked" }, host: { listeners: { "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "chatWindows", predicate: ["chatWindow"], descendants: true }], ngImport: i0, template: "<link *ngIf=\"customTheme\" rel=\"stylesheet\" [href]='customTheme | sanitize'>\n\n<div id=\"ng-chat\" *ngIf=\"!isDisabled && isBootstrapped && !unsupportedViewport\" [ngClass]=\"theme\">\n <ng-chat-friends-list\n [localization]=\"localization\"\n [shouldDisplay]=\"!hideFriendsList\"\n [userId]=\"userId\"\n [isCollapsed]=\"isCollapsed\"\n [searchEnabled]=\"searchEnabled\"\n [participants]=\"participants\"\n [participantsResponse]=\"participantsResponse\"\n [participantsInteractedWith]=\"participantsInteractedWith\"\n [windows]=\"windows\"\n [currentActiveOption]=\"currentActiveOption\"\n (onParticipantClicked)=\"onParticipantClickedFromFriendsList($event)\"\n (onOptionPromptCanceled)=\"onOptionPromptCanceled()\"\n (onOptionPromptConfirmed)=\"onOptionPromptConfirmed($event)\"\n (onCloseWindowClick)=\"onCloseWindowClick($event)\"\n >\n </ng-chat-friends-list>\n\n <div *ngFor=\"let window of windows; let i = index\" [ngClass]=\"{'ng-chat-window': true, 'primary-outline-color': true, 'ng-chat-window-collapsed': window.isCollapsed}\" [ngStyle]=\"{'right': (!hideFriendsList ? friendsListWidth : 0) + 20 + windowSizeFactor * i + 'px'}\">\n <ng-chat-window\n #chatWindow\n [fileUploadAdapter]=\"fileUploadAdapter\"\n [localization]=\"localization\"\n [userId]=\"userId\"\n [window]=\"window\"\n [showOptions]=\"groupAdapter\"\n [emojisEnabled]=\"emojisEnabled\"\n [linkfyEnabled]=\"linkfyEnabled\"\n [showMessageDate]=\"showMessageDate\"\n [messageDatePipeFormat]=\"messageDatePipeFormat\"\n [hasPagedHistory]=\"hasPagedHistory\"\n (onMessagesSeen)=\"onWindowMessagesSeen($event)\"\n (onMessageSent)=\"onWindowMessageSent($event)\"\n (onTabTriggered)=\"onWindowTabTriggered($event)\"\n (onChatWindowClosed)=\"onWindowChatClosed($event)\"\n (onOptionTriggered)=\"onWindowOptionTriggered($event)\"\n (onLoadHistoryTriggered)=\"fetchMessageHistory($event)\"\n >\n </ng-chat-window>\n </div>\n</div>\n", styles: [".user-icon{box-sizing:border-box;background-color:#fff;border:2px solid;width:32px;height:20px;border-radius:64px 64px 0 0/64px;margin-top:14px;margin-left:-1px;display:inline-block;vertical-align:middle;position:relative;font-style:normal;color:#ddd;text-align:left;text-indent:-9999px}.user-icon:before{border:2px solid;background-color:#fff;width:12px;height:12px;top:-19px;border-radius:50%;position:absolute;left:50%;transform:translate(-50%)}.user-icon:before,.user-icon:after{content:\"\";pointer-events:none}.upload-icon{position:absolute;margin-left:3px;margin-top:12px;width:13px;height:4px;border:solid 1px currentColor;border-top:none;border-radius:1px}.upload-icon:before{content:\"\";position:absolute;top:-8px;left:6px;width:1px;height:9px;background-color:currentColor}.upload-icon:after{content:\"\";position:absolute;top:-8px;left:4px;width:4px;height:4px;border-top:solid 1px currentColor;border-right:solid 1px currentColor;transform:rotate(-45deg)}.paperclip-icon{position:absolute;margin-left:9px;margin-top:2px;width:6px;height:12px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor;transform:rotate(45deg)}.paperclip-icon:before{content:\"\";position:absolute;top:11px;left:-1px;width:4px;height:6px;border-radius:0 0 3px 3px;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-bottom:solid 1px currentColor}.paperclip-icon:after{content:\"\";position:absolute;left:1px;top:1px;width:2px;height:10px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor}.check-icon{color:#000;position:absolute;margin-left:3px;margin-top:4px;width:14px;height:8px;border-bottom:solid 1px currentColor;border-left:solid 1px currentColor;transform:rotate(-45deg)}.remove-icon{color:#000;position:absolute;margin-left:3px;margin-top:10px}.remove-icon:before{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;transform:rotate(45deg)}.remove-icon:after{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;transform:rotate(-45deg)}\n", ".loader,.loader:before,.loader:after{background:#E3E3E3;animation:load1 1s infinite ease-in-out;width:1em;height:4em}.loader{color:#e3e3e3;text-indent:-9999em;margin:4px auto 0;position:relative;font-size:4px;transform:translateZ(0);animation-delay:-.16s}.loader:before,.loader:after{position:absolute;top:0;content:\"\"}.loader:before{left:-1.5em;animation-delay:-.32s}.loader:after{left:1.5em}@keyframes load1{0%,80%,to{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}\n", "#ng-chat{position:fixed;z-index:999;right:0;bottom:0;box-sizing:initial;font-size:11pt;text-align:left}#ng-chat input{outline:none}#ng-chat .shadowed{box-shadow:0 4px 8px #00000040}.ng-chat-loading-wrapper{height:30px;text-align:center;font-size:.9em}.ng-chat-close{text-decoration:none!important;color:red!important;float:right!important;font-weight:900!important;font-size:15px!important}.ng-chat-title,.ng-chat-title:hover{position:relative;z-index:2;height:30px;line-height:30px;font-size:.9em;padding:0 10px;display:block;text-decoration:none;color:#fff!important;font-weight:600!important;cursor:pointer;background-color:#1b1b70!important}.ng-chat-title .ng-chat-title-visibility-toggle-area{display:inline-block;width:85%}.ng-chat-title .ng-chat-title-visibility-toggle-area>strong{font-weight:600;display:block;overflow:hidden;height:30px;text-overflow:ellipsis;white-space:nowrap;max-width:85%;float:left}.ng-chat-title .ng-chat-title-visibility-toggle-area .ng-chat-participant-status{float:left;margin-left:5px}.ng-chat-participant-status{display:inline-block;border-radius:25px;width:8px;height:8px;margin-top:10px}.ng-chat-participant-status.online{background-color:#92a400}.ng-chat-participant-status.busy{background-color:#f91c1e}.ng-chat-participant-status.away{background-color:#f7d21b}.ng-chat-participant-status.offline{background-color:#bababa}.ng-chat-unread-messages-count{margin-left:5px;padding:0 5px;border-radius:25px;font-size:.9em;line-height:30px}.ng-chat-options-container{float:right;margin-right:5px}\n", "#ng-chat.light-theme .primary-text,#ng-chat.light-theme{color:#5c5c5c;font-family:Arial,Helvetica,sans-serif}#ng-chat.light-theme .primary-background{background-color:#fff}#ng-chat.light-theme .secondary-background{background-color:#fafafa}#ng-chat.light-theme .primary-outline-color{border-color:#a3a3a3}#ng-chat.light-theme .friends-search-bar{background-color:#fff}#ng-chat.light-theme .unread-messages-counter-container,#ng-chat.light-theme .ng-chat-people-action,#ng-chat.light-theme .ng-chat-people-action>i{color:#5c5c5c;background-color:#e3e3e3}#ng-chat.light-theme .load-history-action{background-color:#e3e3e3}#ng-chat.light-theme .chat-window-input{background-color:#fff}#ng-chat.light-theme .sent-chat-message-container,#ng-chat.light-theme .file-message-container{background-color:#e3e3e3;border-color:#e3e3e3}#ng-chat.light-theme .received-chat-message-container,#ng-chat.light-theme .file-message-container.received{background-color:#fff;border-color:#e3e3e3}body{font-family:Roboto,Helvetica Neue,sans-serif!important}*{box-shadow:none!important}\n", "#ng-chat.dark-theme .primary-text,#ng-chat.dark-theme{color:#fff;font-family:Arial,Helvetica,sans-serif}#ng-chat.dark-theme .primary-background{background-color:#565656}#ng-chat.dark-theme .secondary-background{background-color:#444}#ng-chat.dark-theme .primary-outline-color{border-color:#353535}#ng-chat.dark-theme .friends-search-bar{background-color:#444;border:1px solid #444;color:#fff}#ng-chat.dark-theme .unread-messages-counter-container,#ng-chat.dark-theme .ng-chat-people-action,#ng-chat.dark-theme .ng-chat-people-action>i{background-color:#fff;color:#444}#ng-chat.dark-theme .load-history-action{background-color:#444}#ng-chat.dark-theme .chat-window-input{background-color:#444;color:#fff}#ng-chat.dark-theme .sent-chat-message-container,#ng-chat.dark-theme .file-message-container{border-color:#444;background-color:#444}#ng-chat.dark-theme .received-chat-message-container,#ng-chat.dark-theme .file-message-container.received{background-color:#565656;border-color:#444}#ng-chat.dark-theme .ng-chat-footer{background-color:#444}#ng-chat.dark-theme .ng-chat-message a{color:#fff}body{font-family:Roboto,Helvetica Neue,sans-serif!important}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: NgChatFriendsListComponent, selector: "ng-chat-friends-list", inputs: ["participants", "participantsResponse", "participantsInteractedWith", "windows", "userId", "localization", "shouldDisplay", "isCollapsed", "searchEnabled", "currentActiveOption", "hideFriendsList"], outputs: ["onParticipantClicked", "onOptionPromptCanceled", "onOptionPromptConfirmed", "onCloseWindowClick"] }, { kind: "component", type: NgChatWindowComponent, selector: "ng-chat-window", inputs: ["fileUploadAdapter", "window", "userId", "localization", "showOptions", "emojisEnabled", "linkfyEnabled", "showMessageDate", "messageDatePipeFormat", "hasPagedHistory"], outputs: ["onChatWindowClosed", "onMessagesSeen", "onMessageSent", "onTabTriggered", "onOptionTriggered", "onLoadHistoryTriggered"] }, { kind: "pipe", type: SanitizePipe, name: "sanitize" }], encapsulation: i0.ViewEncapsulation.None });
1219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChat, decorators: [{
1220
+ type: Component,
1221
+ args: [{ selector: 'mnl-chat', encapsulation: ViewEncapsulation.None, template: "<link *ngIf=\"customTheme\" rel=\"stylesheet\" [href]='customTheme | sanitize'>\n\n<div id=\"ng-chat\" *ngIf=\"!isDisabled && isBootstrapped && !unsupportedViewport\" [ngClass]=\"theme\">\n <ng-chat-friends-list\n [localization]=\"localization\"\n [shouldDisplay]=\"!hideFriendsList\"\n [userId]=\"userId\"\n [isCollapsed]=\"isCollapsed\"\n [searchEnabled]=\"searchEnabled\"\n [participants]=\"participants\"\n [participantsResponse]=\"participantsResponse\"\n [participantsInteractedWith]=\"participantsInteractedWith\"\n [windows]=\"windows\"\n [currentActiveOption]=\"currentActiveOption\"\n (onParticipantClicked)=\"onParticipantClickedFromFriendsList($event)\"\n (onOptionPromptCanceled)=\"onOptionPromptCanceled()\"\n (onOptionPromptConfirmed)=\"onOptionPromptConfirmed($event)\"\n (onCloseWindowClick)=\"onCloseWindowClick($event)\"\n >\n </ng-chat-friends-list>\n\n <div *ngFor=\"let window of windows; let i = index\" [ngClass]=\"{'ng-chat-window': true, 'primary-outline-color': true, 'ng-chat-window-collapsed': window.isCollapsed}\" [ngStyle]=\"{'right': (!hideFriendsList ? friendsListWidth : 0) + 20 + windowSizeFactor * i + 'px'}\">\n <ng-chat-window\n #chatWindow\n [fileUploadAdapter]=\"fileUploadAdapter\"\n [localization]=\"localization\"\n [userId]=\"userId\"\n [window]=\"window\"\n [showOptions]=\"groupAdapter\"\n [emojisEnabled]=\"emojisEnabled\"\n [linkfyEnabled]=\"linkfyEnabled\"\n [showMessageDate]=\"showMessageDate\"\n [messageDatePipeFormat]=\"messageDatePipeFormat\"\n [hasPagedHistory]=\"hasPagedHistory\"\n (onMessagesSeen)=\"onWindowMessagesSeen($event)\"\n (onMessageSent)=\"onWindowMessageSent($event)\"\n (onTabTriggered)=\"onWindowTabTriggered($event)\"\n (onChatWindowClosed)=\"onWindowChatClosed($event)\"\n (onOptionTriggered)=\"onWindowOptionTriggered($event)\"\n (onLoadHistoryTriggered)=\"fetchMessageHistory($event)\"\n >\n </ng-chat-window>\n </div>\n</div>\n", styles: [".user-icon{box-sizing:border-box;background-color:#fff;border:2px solid;width:32px;height:20px;border-radius:64px 64px 0 0/64px;margin-top:14px;margin-left:-1px;display:inline-block;vertical-align:middle;position:relative;font-style:normal;color:#ddd;text-align:left;text-indent:-9999px}.user-icon:before{border:2px solid;background-color:#fff;width:12px;height:12px;top:-19px;border-radius:50%;position:absolute;left:50%;transform:translate(-50%)}.user-icon:before,.user-icon:after{content:\"\";pointer-events:none}.upload-icon{position:absolute;margin-left:3px;margin-top:12px;width:13px;height:4px;border:solid 1px currentColor;border-top:none;border-radius:1px}.upload-icon:before{content:\"\";position:absolute;top:-8px;left:6px;width:1px;height:9px;background-color:currentColor}.upload-icon:after{content:\"\";position:absolute;top:-8px;left:4px;width:4px;height:4px;border-top:solid 1px currentColor;border-right:solid 1px currentColor;transform:rotate(-45deg)}.paperclip-icon{position:absolute;margin-left:9px;margin-top:2px;width:6px;height:12px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor;transform:rotate(45deg)}.paperclip-icon:before{content:\"\";position:absolute;top:11px;left:-1px;width:4px;height:6px;border-radius:0 0 3px 3px;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-bottom:solid 1px currentColor}.paperclip-icon:after{content:\"\";position:absolute;left:1px;top:1px;width:2px;height:10px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor}.check-icon{color:#000;position:absolute;margin-left:3px;margin-top:4px;width:14px;height:8px;border-bottom:solid 1px currentColor;border-left:solid 1px currentColor;transform:rotate(-45deg)}.remove-icon{color:#000;position:absolute;margin-left:3px;margin-top:10px}.remove-icon:before{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;transform:rotate(45deg)}.remove-icon:after{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;transform:rotate(-45deg)}\n", ".loader,.loader:before,.loader:after{background:#E3E3E3;animation:load1 1s infinite ease-in-out;width:1em;height:4em}.loader{color:#e3e3e3;text-indent:-9999em;margin:4px auto 0;position:relative;font-size:4px;transform:translateZ(0);animation-delay:-.16s}.loader:before,.loader:after{position:absolute;top:0;content:\"\"}.loader:before{left:-1.5em;animation-delay:-.32s}.loader:after{left:1.5em}@keyframes load1{0%,80%,to{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}\n", "#ng-chat{position:fixed;z-index:999;right:0;bottom:0;box-sizing:initial;font-size:11pt;text-align:left}#ng-chat input{outline:none}#ng-chat .shadowed{box-shadow:0 4px 8px #00000040}.ng-chat-loading-wrapper{height:30px;text-align:center;font-size:.9em}.ng-chat-close{text-decoration:none!important;color:red!important;float:right!important;font-weight:900!important;font-size:15px!important}.ng-chat-title,.ng-chat-title:hover{position:relative;z-index:2;height:30px;line-height:30px;font-size:.9em;padding:0 10px;display:block;text-decoration:none;color:#fff!important;font-weight:600!important;cursor:pointer;background-color:#1b1b70!important}.ng-chat-title .ng-chat-title-visibility-toggle-area{display:inline-block;width:85%}.ng-chat-title .ng-chat-title-visibility-toggle-area>strong{font-weight:600;display:block;overflow:hidden;height:30px;text-overflow:ellipsis;white-space:nowrap;max-width:85%;float:left}.ng-chat-title .ng-chat-title-visibility-toggle-area .ng-chat-participant-status{float:left;margin-left:5px}.ng-chat-participant-status{display:inline-block;border-radius:25px;width:8px;height:8px;margin-top:10px}.ng-chat-participant-status.online{background-color:#92a400}.ng-chat-participant-status.busy{background-color:#f91c1e}.ng-chat-participant-status.away{background-color:#f7d21b}.ng-chat-participant-status.offline{background-color:#bababa}.ng-chat-unread-messages-count{margin-left:5px;padding:0 5px;border-radius:25px;font-size:.9em;line-height:30px}.ng-chat-options-container{float:right;margin-right:5px}\n", "#ng-chat.light-theme .primary-text,#ng-chat.light-theme{color:#5c5c5c;font-family:Arial,Helvetica,sans-serif}#ng-chat.light-theme .primary-background{background-color:#fff}#ng-chat.light-theme .secondary-background{background-color:#fafafa}#ng-chat.light-theme .primary-outline-color{border-color:#a3a3a3}#ng-chat.light-theme .friends-search-bar{background-color:#fff}#ng-chat.light-theme .unread-messages-counter-container,#ng-chat.light-theme .ng-chat-people-action,#ng-chat.light-theme .ng-chat-people-action>i{color:#5c5c5c;background-color:#e3e3e3}#ng-chat.light-theme .load-history-action{background-color:#e3e3e3}#ng-chat.light-theme .chat-window-input{background-color:#fff}#ng-chat.light-theme .sent-chat-message-container,#ng-chat.light-theme .file-message-container{background-color:#e3e3e3;border-color:#e3e3e3}#ng-chat.light-theme .received-chat-message-container,#ng-chat.light-theme .file-message-container.received{background-color:#fff;border-color:#e3e3e3}body{font-family:Roboto,Helvetica Neue,sans-serif!important}*{box-shadow:none!important}\n", "#ng-chat.dark-theme .primary-text,#ng-chat.dark-theme{color:#fff;font-family:Arial,Helvetica,sans-serif}#ng-chat.dark-theme .primary-background{background-color:#565656}#ng-chat.dark-theme .secondary-background{background-color:#444}#ng-chat.dark-theme .primary-outline-color{border-color:#353535}#ng-chat.dark-theme .friends-search-bar{background-color:#444;border:1px solid #444;color:#fff}#ng-chat.dark-theme .unread-messages-counter-container,#ng-chat.dark-theme .ng-chat-people-action,#ng-chat.dark-theme .ng-chat-people-action>i{background-color:#fff;color:#444}#ng-chat.dark-theme .load-history-action{background-color:#444}#ng-chat.dark-theme .chat-window-input{background-color:#444;color:#fff}#ng-chat.dark-theme .sent-chat-message-container,#ng-chat.dark-theme .file-message-container{border-color:#444;background-color:#444}#ng-chat.dark-theme .received-chat-message-container,#ng-chat.dark-theme .file-message-container.received{background-color:#565656;border-color:#444}#ng-chat.dark-theme .ng-chat-footer{background-color:#444}#ng-chat.dark-theme .ng-chat-message a{color:#fff}body{font-family:Roboto,Helvetica Neue,sans-serif!important}\n"] }]
1222
+ }], ctorParameters: function () { return [{ type: i1$2.HttpClient }]; }, propDecorators: { isDisabled: [{
1223
+ type: Input
1224
+ }], adapter: [{
1225
+ type: Input
1226
+ }], groupAdapter: [{
1227
+ type: Input
1228
+ }], userId: [{
1229
+ type: Input
1230
+ }], isCollapsed: [{
1231
+ type: Input
1232
+ }], maximizeWindowOnNewMessage: [{
1233
+ type: Input
1234
+ }], pollFriendsList: [{
1235
+ type: Input
1236
+ }], pollingInterval: [{
1237
+ type: Input
1238
+ }], historyEnabled: [{
1239
+ type: Input
1240
+ }], emojisEnabled: [{
1241
+ type: Input
1242
+ }], linkfyEnabled: [{
1243
+ type: Input
1244
+ }], audioEnabled: [{
1245
+ type: Input
1246
+ }], searchEnabled: [{
1247
+ type: Input
1248
+ }], audioSource: [{
1249
+ type: Input
1250
+ }], persistWindowsState: [{
1251
+ type: Input
1252
+ }], title: [{
1253
+ type: Input
1254
+ }], messagePlaceholder: [{
1255
+ type: Input
1256
+ }], searchPlaceholder: [{
1257
+ type: Input
1258
+ }], browserNotificationsEnabled: [{
1259
+ type: Input
1260
+ }], browserNotificationIconSource: [{
1261
+ type: Input
1262
+ }], browserNotificationTitle: [{
1263
+ type: Input
1264
+ }], historyPageSize: [{
1265
+ type: Input
1266
+ }], localization: [{
1267
+ type: Input
1268
+ }], hideFriendsList: [{
1269
+ type: Input
1270
+ }], hideFriendsListOnUnsupportedViewport: [{
1271
+ type: Input
1272
+ }], fileUploadUrl: [{
1273
+ type: Input
1274
+ }], theme: [{
1275
+ type: Input
1276
+ }], customTheme: [{
1277
+ type: Input
1278
+ }], messageDatePipeFormat: [{
1279
+ type: Input
1280
+ }], showMessageDate: [{
1281
+ type: Input
1282
+ }], isViewportOnMobileEnabled: [{
1283
+ type: Input
1284
+ }], fileUploadAdapter: [{
1285
+ type: Input
1286
+ }], onParticipantClicked: [{
1287
+ type: Output
1288
+ }], onParticipantChatOpened: [{
1289
+ type: Output
1290
+ }], onParticipantChatClosed: [{
1291
+ type: Output
1292
+ }], onMessagesSeen: [{
1293
+ type: Output
1294
+ }], onCloseWindowClicked: [{
1295
+ type: Output
1296
+ }], chatWindows: [{
1297
+ type: ViewChildren,
1298
+ args: ['chatWindow']
1299
+ }], onResize: [{
1300
+ type: HostListener,
1301
+ args: ['window:resize', ['$event']]
1302
+ }] } });
1303
+
1304
+ /* eslint-disable */
1305
+ class NgChatOptionsComponent {
1306
+ constructor() {
1307
+ this.activeOptionTrackerChange = new EventEmitter();
1308
+ }
1309
+ onOptionClicked(option) {
1310
+ option.isActive = true;
1311
+ if (option.action) {
1312
+ option.action(option.chattingTo);
1313
+ }
1314
+ this.activeOptionTrackerChange.emit(option);
1315
+ }
1316
+ }
1317
+ NgChatOptionsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChatOptionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1318
+ NgChatOptionsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NgChatOptionsComponent, selector: "ng-chat-options", inputs: { options: "options", activeOptionTracker: "activeOptionTracker" }, outputs: { activeOptionTrackerChange: "activeOptionTrackerChange" }, ngImport: i0, template: "<div *ngIf=\"options && options.length > 0\" class=\"ng-chat-options\">\n\t\t<button class=\"ng-chat-options-activator\">\n\t\t\t<span class=\"primary-text\">...</span>\n\t\t</button>\n\t<div class=\"ng-chat-options-content primary-background shadowed\">\n\t\t<a *ngFor=\"let option of options; let i = index\" [ngClass]=\"'primary-text'\" (click)=\"onOptionClicked(option)\">\n\t\t\t{{option.displayLabel}}\n\t\t</a>\n\t</div> \n</div>\n", styles: [".ng-chat-options-activator{background-color:unset;color:#fff;line-height:28px;border:none;position:relative}.ng-chat-options-activator>span{position:relative;top:-5px;left:0}.ng-chat-options{position:relative;display:inline-block}.ng-chat-options:hover .ng-chat-options-content{display:block}.ng-chat-options:hover .ng-chat-options-activator{background-color:#ddd}.ng-chat-options-content{display:none;position:absolute;min-width:160px;z-index:1}.ng-chat-options-content a:hover{background-color:#ddd}.ng-chat-options-content a{padding:6px 16px;text-decoration:none;display:block}@media only screen and (max-width: 581px){.ng-chat-options-content{right:0}}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
1319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgChatOptionsComponent, decorators: [{
1320
+ type: Component,
1321
+ args: [{ selector: 'ng-chat-options', template: "<div *ngIf=\"options && options.length > 0\" class=\"ng-chat-options\">\n\t\t<button class=\"ng-chat-options-activator\">\n\t\t\t<span class=\"primary-text\">...</span>\n\t\t</button>\n\t<div class=\"ng-chat-options-content primary-background shadowed\">\n\t\t<a *ngFor=\"let option of options; let i = index\" [ngClass]=\"'primary-text'\" (click)=\"onOptionClicked(option)\">\n\t\t\t{{option.displayLabel}}\n\t\t</a>\n\t</div> \n</div>\n", styles: [".ng-chat-options-activator{background-color:unset;color:#fff;line-height:28px;border:none;position:relative}.ng-chat-options-activator>span{position:relative;top:-5px;left:0}.ng-chat-options{position:relative;display:inline-block}.ng-chat-options:hover .ng-chat-options-content{display:block}.ng-chat-options:hover .ng-chat-options-activator{background-color:#ddd}.ng-chat-options-content{display:none;position:absolute;min-width:160px;z-index:1}.ng-chat-options-content a:hover{background-color:#ddd}.ng-chat-options-content a{padding:6px 16px;text-decoration:none;display:block}@media only screen and (max-width: 581px){.ng-chat-options-content{right:0}}\n"] }]
1322
+ }], ctorParameters: function () { return []; }, propDecorators: { options: [{
1323
+ type: Input
1324
+ }], activeOptionTracker: [{
1325
+ type: Input
1326
+ }], activeOptionTrackerChange: [{
1327
+ type: Output
1328
+ }] } });
1329
+
1330
+ class ChatModule {
1331
+ static forRoot() {
1332
+ return {
1333
+ ngModule: ChatModule,
1334
+ providers: [
1335
+ {
1336
+ provide: APP_INITIALIZER,
1337
+ useFactory: loadConfig,
1338
+ deps: [
1339
+ AppConfigService,
1340
+ ChatService
1341
+ ],
1342
+ multi: true,
1343
+ },
1344
+ ],
1345
+ };
1346
+ }
1347
+ static forChild() {
1348
+ return {
1349
+ ngModule: ChatModule,
1350
+ };
1351
+ }
1352
+ }
1353
+ ChatModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1354
+ ChatModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: ChatModule, declarations: [NgChat,
1355
+ EmojifyPipe,
1356
+ LinkfyPipe,
1357
+ SanitizePipe,
1358
+ GroupMessageDisplayNamePipe,
1359
+ NgChatOptionsComponent,
1360
+ NgChatFriendsListComponent,
1361
+ NgChatWindowComponent,
1362
+ FirstLetterPipe], imports: [ChatMaterialModule,
1363
+ CommonModule,
1364
+ FormsModule,
1365
+ HttpClientModule], exports: [NgChat] });
1366
+ ChatModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatModule, imports: [ChatMaterialModule,
1367
+ CommonModule,
1368
+ FormsModule,
1369
+ HttpClientModule] });
1370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ChatModule, decorators: [{
1371
+ type: NgModule,
1372
+ args: [{
1373
+ declarations: [
1374
+ NgChat,
1375
+ EmojifyPipe,
1376
+ LinkfyPipe,
1377
+ SanitizePipe,
1378
+ GroupMessageDisplayNamePipe,
1379
+ NgChatOptionsComponent,
1380
+ NgChatFriendsListComponent,
1381
+ NgChatWindowComponent,
1382
+ FirstLetterPipe
1383
+ ],
1384
+ imports: [
1385
+ ChatMaterialModule,
1386
+ CommonModule,
1387
+ FormsModule,
1388
+ HttpClientModule
1389
+ ],
1390
+ exports: [
1391
+ NgChat
1392
+ ],
1393
+ }]
1394
+ }] });
1395
+ function loadConfig(config) {
1396
+ const conf = () => config.load();
1397
+ return conf;
1398
+ }
1399
+
1400
+ class User {
1401
+ constructor() {
1402
+ this.participantType = ChatParticipantType.User;
1403
+ }
1404
+ }
1405
+
1406
+ class ParticipantResponse {
1407
+ }
1408
+
1409
+ class ParticipantMetadata {
1410
+ constructor() {
1411
+ this.totalUnreadMessages = 0;
1412
+ }
1413
+ }
1414
+
1415
+ /*
1416
+ * Public API Surface of mnl-chat
1417
+ */
1418
+
1419
+ /**
1420
+ * Generated bundle index. Do not edit.
1421
+ */
1422
+
1423
+ export { ChatAdapter, ChatModule, ChatParticipantStatus, ChatParticipantType, ChatService, Group, Message, MessageType, NgChat, PagedHistoryChatAdapter, ParticipantMetadata, ParticipantResponse, Theme, User, Window, loadConfig };
1424
+ //# sourceMappingURL=muraai-mnl-chat.mjs.map