quikchat 1.1.17 → 1.2.4

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 (43) hide show
  1. package/README.md +167 -276
  2. package/dist/build-manifest.json +157 -0
  3. package/dist/quikchat-md-full.cjs.js +2742 -0
  4. package/dist/quikchat-md-full.cjs.js.map +1 -0
  5. package/dist/quikchat-md-full.cjs.min.js +10 -0
  6. package/dist/quikchat-md-full.cjs.min.js.map +1 -0
  7. package/dist/quikchat-md-full.esm.js +2740 -0
  8. package/dist/quikchat-md-full.esm.js.map +1 -0
  9. package/dist/quikchat-md-full.esm.min.js +10 -0
  10. package/dist/quikchat-md-full.esm.min.js.map +1 -0
  11. package/dist/quikchat-md-full.umd.js +2748 -0
  12. package/dist/quikchat-md-full.umd.js.map +1 -0
  13. package/dist/quikchat-md-full.umd.min.js +10 -0
  14. package/dist/quikchat-md-full.umd.min.js.map +1 -0
  15. package/dist/quikchat-md.cjs.js +1641 -0
  16. package/dist/quikchat-md.cjs.js.map +1 -0
  17. package/dist/quikchat-md.cjs.min.js +8 -0
  18. package/dist/quikchat-md.cjs.min.js.map +1 -0
  19. package/dist/quikchat-md.esm.js +1639 -0
  20. package/dist/quikchat-md.esm.js.map +1 -0
  21. package/dist/quikchat-md.esm.min.js +8 -0
  22. package/dist/quikchat-md.esm.min.js.map +1 -0
  23. package/dist/quikchat-md.umd.js +1647 -0
  24. package/dist/quikchat-md.umd.js.map +1 -0
  25. package/dist/quikchat-md.umd.min.js +8 -0
  26. package/dist/quikchat-md.umd.min.js.map +1 -0
  27. package/dist/quikchat.cjs.js +454 -1729
  28. package/dist/quikchat.cjs.js.map +1 -1
  29. package/dist/quikchat.cjs.min.js +1 -1
  30. package/dist/quikchat.cjs.min.js.map +1 -1
  31. package/dist/quikchat.css +753 -226
  32. package/dist/quikchat.esm.js +454 -1729
  33. package/dist/quikchat.esm.js.map +1 -1
  34. package/dist/quikchat.esm.min.js +1 -1
  35. package/dist/quikchat.esm.min.js.map +1 -1
  36. package/dist/quikchat.min.css +1 -1
  37. package/dist/quikchat.react.js +63 -0
  38. package/dist/quikchat.umd.js +454 -1729
  39. package/dist/quikchat.umd.js.map +1 -1
  40. package/dist/quikchat.umd.min.js +1 -1
  41. package/dist/quikchat.umd.min.js.map +1 -1
  42. package/package.json +59 -39
  43. package/dist/quikchat.d.ts +0 -194
@@ -3,37 +3,76 @@ function _arrayLikeToArray(r, a) {
3
3
  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
4
4
  return n;
5
5
  }
6
- function _arrayWithoutHoles(r) {
7
- if (Array.isArray(r)) return _arrayLikeToArray(r);
8
- }
9
6
  function _classCallCheck(a, n) {
10
7
  if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
11
8
  }
12
9
  function _defineProperties(e, r) {
13
10
  for (var t = 0; t < r.length; t++) {
14
11
  var o = r[t];
15
- o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
12
+ o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o);
16
13
  }
17
14
  }
18
15
  function _createClass(e, r, t) {
19
16
  return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
20
- writable: !1
17
+ writable: false
21
18
  }), e;
22
19
  }
20
+ function _createForOfIteratorHelper(r, e) {
21
+ var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
22
+ if (!t) {
23
+ if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
24
+ t && (r = t);
25
+ var n = 0,
26
+ F = function () {};
27
+ return {
28
+ s: F,
29
+ n: function () {
30
+ return n >= r.length ? {
31
+ done: true
32
+ } : {
33
+ done: false,
34
+ value: r[n++]
35
+ };
36
+ },
37
+ e: function (r) {
38
+ throw r;
39
+ },
40
+ f: F
41
+ };
42
+ }
43
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
44
+ }
45
+ var o,
46
+ a = true,
47
+ u = false;
48
+ return {
49
+ s: function () {
50
+ t = t.call(r);
51
+ },
52
+ n: function () {
53
+ var r = t.next();
54
+ return a = r.done, r;
55
+ },
56
+ e: function (r) {
57
+ u = true, o = r;
58
+ },
59
+ f: function () {
60
+ try {
61
+ a || null == t.return || t.return();
62
+ } finally {
63
+ if (u) throw o;
64
+ }
65
+ }
66
+ };
67
+ }
23
68
  function _defineProperty(e, r, t) {
24
69
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
25
70
  value: t,
26
- enumerable: !0,
27
- configurable: !0,
28
- writable: !0
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true
29
74
  }) : e[r] = t, e;
30
75
  }
31
- function _iterableToArray(r) {
32
- if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
33
- }
34
- function _nonIterableSpread() {
35
- throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
36
- }
37
76
  function ownKeys(e, r) {
38
77
  var t = Object.keys(e);
39
78
  if (Object.getOwnPropertySymbols) {
@@ -47,7 +86,7 @@ function ownKeys(e, r) {
47
86
  function _objectSpread2(e) {
48
87
  for (var r = 1; r < arguments.length; r++) {
49
88
  var t = null != arguments[r] ? arguments[r] : {};
50
- r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
89
+ r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
51
90
  _defineProperty(e, r, t[r]);
52
91
  }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
53
92
  Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
@@ -55,18 +94,15 @@ function _objectSpread2(e) {
55
94
  }
56
95
  return e;
57
96
  }
58
- function _toConsumableArray(r) {
59
- return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
60
- }
61
97
  function _toPrimitive(t, r) {
62
98
  if ("object" != typeof t || !t) return t;
63
99
  var e = t[Symbol.toPrimitive];
64
100
  if (void 0 !== e) {
65
- var i = e.call(t, r || "default");
101
+ var i = e.call(t, r);
66
102
  if ("object" != typeof i) return i;
67
103
  throw new TypeError("@@toPrimitive must return a primitive value.");
68
104
  }
69
- return ("string" === r ? String : Number)(t);
105
+ return (String )(t);
70
106
  }
71
107
  function _toPropertyKey(t) {
72
108
  var i = _toPrimitive(t, "string");
@@ -80,378 +116,11 @@ function _unsupportedIterableToArray(r, a) {
80
116
  }
81
117
  }
82
118
 
83
- // Auto-generated version file - DO NOT EDIT MANUALLY
84
- // This file is automatically updated by tools/updateVersion.js
85
-
86
- var quikchatVersion = {
87
- version: "1.1.17",
88
- license: "BSD-2",
89
- url: "https://github/deftio/quikchat",
90
- fun: true
91
- };
92
-
93
- /**
94
- * Simplified virtual scrolling implementation for QuikChat
95
- * @private
96
- */
97
- var SimpleVirtualScroller = /*#__PURE__*/function () {
98
- function SimpleVirtualScroller(container) {
99
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
100
- _classCallCheck(this, SimpleVirtualScroller);
101
- this.container = container;
102
- this.items = [];
103
- this.itemHeight = options.itemHeight || 80; // Default/estimate height
104
- this.buffer = options.buffer || 5;
105
- this.visibleRange = {
106
- start: 0,
107
- end: 0
108
- };
109
- this.renderedElements = new Map();
110
- this.itemHeights = new Map(); // Cache actual heights
111
- this.itemPositions = new Map(); // Cache positions
112
- this.totalHeight = 0;
113
- this.onRenderItem = options.onRenderItem || function () {};
114
- this.sanitizer = options.sanitizer || null; // Content sanitizer
115
-
116
- this._initStructure();
117
- this._attachScrollListener();
118
- }
119
- return _createClass(SimpleVirtualScroller, [{
120
- key: "_initStructure",
121
- value: function _initStructure() {
122
- var _this = this;
123
- var existingClasses = this.container.className;
124
- this.container.innerHTML = '';
125
- this.container.className = existingClasses;
126
- this.container.style.position = 'relative';
127
- this.container.style.overflow = 'auto';
128
-
129
- // Ensure container has height
130
- if (this.container.offsetHeight === 0) {
131
- // Try to get height from computed style or parent
132
- var computedHeight = window.getComputedStyle(this.container).height;
133
- if (computedHeight === '0px' || computedHeight === 'auto') {
134
- // If still no height, use a reasonable default
135
- this.container.style.height = '400px';
136
- console.warn('QuikChat Virtual Scrolling: Container has no height, setting to 400px');
137
- }
138
- }
139
- this.spacer = document.createElement('div');
140
- this.spacer.style.cssText = 'position: absolute; top: 0; left: 0; width: 1px; pointer-events: none; z-index: -1;';
141
- this.content = document.createElement('div');
142
- this.content.style.cssText = 'position: relative; width: 100%;';
143
- this.container.appendChild(this.spacer);
144
- this.container.appendChild(this.content);
145
-
146
- // Initial render after structure is set up
147
- setTimeout(function () {
148
- _this._updateVisibleRange();
149
- _this._renderVisibleItems();
150
- }, 0);
151
- }
152
- }, {
153
- key: "_attachScrollListener",
154
- value: function _attachScrollListener() {
155
- var _this2 = this;
156
- var ticking = false;
157
- this.container.addEventListener('scroll', function () {
158
- if (!ticking) {
159
- requestAnimationFrame(function () {
160
- _this2._updateVisibleRange();
161
- _this2._renderVisibleItems();
162
- ticking = false;
163
- });
164
- ticking = true;
165
- }
166
- });
167
- }
168
- }, {
169
- key: "_getItemHeight",
170
- value: function _getItemHeight(index) {
171
- // Return cached height or estimate
172
- return this.itemHeights.get(index) || this.itemHeight;
173
- }
174
- }, {
175
- key: "_getItemPosition",
176
- value: function _getItemPosition(index) {
177
- // Return cached position or calculate
178
- if (this.itemPositions.has(index)) {
179
- return this.itemPositions.get(index);
180
- }
181
-
182
- // Calculate position based on previous items
183
- var position = 0;
184
- for (var i = 0; i < index; i++) {
185
- position += this._getItemHeight(i);
186
- }
187
- this.itemPositions.set(index, position);
188
- return position;
189
- }
190
- }, {
191
- key: "_recalculatePositions",
192
- value: function _recalculatePositions(fromIndex) {
193
- // Recalculate positions from a specific index onwards
194
- var position = fromIndex > 0 ? this._getItemPosition(fromIndex) : 0;
195
- for (var i = fromIndex; i < this.items.length; i++) {
196
- this.itemPositions.set(i, position);
197
- position += this._getItemHeight(i);
198
-
199
- // Update position of rendered elements
200
- var element = this.renderedElements.get(i);
201
- if (element) {
202
- element.style.top = "".concat(this.itemPositions.get(i), "px");
203
- }
204
- }
205
-
206
- // Update total height
207
- this.totalHeight = position;
208
- this.spacer.style.height = "".concat(this.totalHeight, "px");
209
- }
210
- }, {
211
- key: "_updateVisibleRange",
212
- value: function _updateVisibleRange() {
213
- var scrollTop = this.container.scrollTop;
214
- var viewportHeight = this.container.clientHeight;
215
-
216
- // If container has no height, don't render anything (avoid infinite loop)
217
- if (viewportHeight === 0) {
218
- this.visibleRange = {
219
- start: 0,
220
- end: 0
221
- };
222
- return;
223
- }
224
-
225
- // Find first visible item based on positions
226
- var startIndex = 0;
227
- var endIndex = this.items.length;
228
-
229
- // Use cached positions when available
230
- if (this.itemPositions.size > 0) {
231
- // Find start index
232
- for (var i = 0; i < this.items.length; i++) {
233
- var pos = this._getItemPosition(i);
234
- if (pos + this._getItemHeight(i) > scrollTop) {
235
- startIndex = Math.max(0, i - this.buffer);
236
- break;
237
- }
238
- }
239
-
240
- // Find end index
241
- for (var _i = startIndex; _i < this.items.length; _i++) {
242
- var _pos = this._getItemPosition(_i);
243
- if (_pos > scrollTop + viewportHeight) {
244
- endIndex = Math.min(this.items.length, _i + this.buffer);
245
- break;
246
- }
247
- }
248
- } else {
249
- // Fallback to estimate if no positions cached yet
250
- startIndex = Math.max(0, Math.floor(scrollTop / this.itemHeight) - this.buffer);
251
- endIndex = Math.min(this.items.length, Math.ceil((scrollTop + viewportHeight) / this.itemHeight) + this.buffer);
252
- }
253
- this.visibleRange = {
254
- start: startIndex,
255
- end: endIndex
256
- };
257
- }
258
- }, {
259
- key: "_renderVisibleItems",
260
- value: function _renderVisibleItems() {
261
- var _this3 = this;
262
- var _this$visibleRange = this.visibleRange,
263
- start = _this$visibleRange.start,
264
- end = _this$visibleRange.end;
265
-
266
- // Remove elements outside range
267
- this.renderedElements.forEach(function (element, index) {
268
- if (index < start || index >= end) {
269
- element.remove();
270
- _this3.renderedElements["delete"](index);
271
- // Don't clear height cache - we might need it again
272
- }
273
- });
274
-
275
- // Add new visible elements
276
- var _loop = function _loop(i) {
277
- var item = _this3.items[i];
278
- if (!item || _this3.renderedElements.has(i)) return 1; // continue
279
- var element = _this3._createItemElement(item, i);
280
- element.style.position = 'absolute';
281
- element.style.top = "".concat(_this3._getItemPosition(i), "px");
282
- element.style.left = '0';
283
- element.style.right = '0';
284
- _this3.renderedElements.set(i, element);
285
- _this3.content.appendChild(element);
286
-
287
- // Measure actual height after rendering
288
- requestAnimationFrame(function () {
289
- if (_this3.renderedElements.has(i)) {
290
- var actualHeight = element.offsetHeight;
291
- if (actualHeight && actualHeight !== _this3._getItemHeight(i)) {
292
- _this3.itemHeights.set(i, actualHeight);
293
- _this3._recalculatePositions(i);
294
- }
295
- }
296
- });
297
- };
298
- for (var i = start; i < end; i++) {
299
- if (_loop(i)) continue;
300
- }
301
- this.onRenderItem(this.content);
302
- }
303
- }, {
304
- key: "_createItemElement",
305
- value: function _createItemElement(item, index) {
306
- var messageDiv = document.createElement('div');
307
- messageDiv.className = "quikchat-message quikchat-message-".concat(item.align || 'left', " quikchat-msgid-").concat(String(item.msgid).padStart(10, '0'));
308
- messageDiv.setAttribute('data-index', index);
309
- messageDiv.setAttribute('data-msgid', item.msgid);
310
- messageDiv.setAttribute('role', 'article');
311
- messageDiv.setAttribute('aria-label', "Message from ".concat(item.userString || 'user'));
312
- if (item.tags && item.tags.length > 0) {
313
- item.tags.forEach(function (tag) {
314
- return messageDiv.classList.add("quikchat-tag-".concat(tag));
315
- });
316
- }
317
- var userDiv = document.createElement('div');
318
- userDiv.className = 'quikchat-message-user';
319
- userDiv.innerHTML = this.sanitizer ? this.sanitizer(item.userString || '') : item.userString || '';
320
- userDiv.setAttribute('aria-label', 'User');
321
- var contentDiv = document.createElement('div');
322
- contentDiv.className = 'quikchat-message-content';
323
- contentDiv.innerHTML = this.sanitizer ? this.sanitizer(item.content || '') : item.content || '';
324
- contentDiv.setAttribute('aria-label', 'Message content');
325
- messageDiv.appendChild(userDiv);
326
- messageDiv.appendChild(contentDiv);
327
- if (!item.visible) {
328
- messageDiv.style.display = 'none';
329
- }
330
- return messageDiv;
331
- }
332
- }, {
333
- key: "addItem",
334
- value: function addItem(item) {
335
- this.items.push(item);
336
- var index = this.items.length - 1;
337
-
338
- // Calculate position for new item
339
- var position = index > 0 ? this._getItemPosition(index - 1) + this._getItemHeight(index - 1) : 0;
340
- this.itemPositions.set(index, position);
341
-
342
- // Update total height (using estimate for new item)
343
- this.totalHeight = position + this.itemHeight;
344
- this.spacer.style.height = "".concat(this.totalHeight, "px");
345
- if (index >= this.visibleRange.start && index < this.visibleRange.end) {
346
- this._renderVisibleItems();
347
- }
348
- if (item.scrollIntoView) {
349
- this.container.scrollTop = this.container.scrollHeight;
350
- }
351
- return index;
352
- }
353
- }, {
354
- key: "addItems",
355
- value: function addItems(items) {
356
- var _this$items;
357
- // Batch add items for better performance
358
- var startLength = this.items.length;
359
- (_this$items = this.items).push.apply(_this$items, _toConsumableArray(items));
360
-
361
- // Calculate positions for new items
362
- var position = startLength > 0 ? this._getItemPosition(startLength - 1) + this._getItemHeight(startLength - 1) : 0;
363
- for (var i = startLength; i < this.items.length; i++) {
364
- this.itemPositions.set(i, position);
365
- position += this.itemHeight; // Use estimate for new items
366
- }
367
- this.totalHeight = position;
368
- this.spacer.style.height = "".concat(this.totalHeight, "px");
369
-
370
- // Update visible range and render
371
- this._updateVisibleRange();
372
- this._renderVisibleItems();
373
-
374
- // Handle scrollIntoView for last item if needed
375
- if (items.length > 0 && items[items.length - 1].scrollIntoView) {
376
- this.container.scrollTop = this.container.scrollHeight;
377
- }
378
- }
379
- }, {
380
- key: "clear",
381
- value: function clear() {
382
- this.items = [];
383
- this.renderedElements.clear();
384
- this.itemHeights.clear();
385
- this.itemPositions.clear();
386
- this.totalHeight = 0;
387
- this.content.innerHTML = '';
388
- this.spacer.style.height = '0px';
389
- this.visibleRange = {
390
- start: 0,
391
- end: 0
392
- };
393
-
394
- // Force update after clear
395
- this._updateVisibleRange();
396
- this._renderVisibleItems();
397
- }
398
- }, {
399
- key: "updateItem",
400
- value: function updateItem(index, updates) {
401
- if (index >= 0 && index < this.items.length) {
402
- this.items[index] = _objectSpread2(_objectSpread2({}, this.items[index]), updates);
403
- if (this.renderedElements.has(index)) {
404
- this._renderVisibleItems();
405
- }
406
- }
407
- }
408
- }, {
409
- key: "destroy",
410
- value: function destroy() {
411
- this.container.innerHTML = '';
412
- this.items = [];
413
- this.renderedElements.clear();
414
- }
415
- }]);
416
- }();
417
- /**
418
- * QuikChat - A zero-dependency JavaScript chat widget for modern web applications
419
- * @class quikchat
420
- */
421
119
  var quikchat = /*#__PURE__*/function () {
422
120
  /**
423
- * Creates a new QuikChat instance
424
- * @constructor
425
- * @param {string|HTMLElement} parentElement - CSS selector or DOM element to attach the chat widget to
426
- * @param {Function} [onSend] - Callback function triggered when user sends a message
427
- * @param {Object} [options] - Configuration options
428
- * @param {string} [options.theme='quikchat-theme-light'] - CSS theme class name
429
- * @param {boolean} [options.trackHistory=true] - Whether to track message history
430
- * @param {Object} [options.titleArea] - Title area configuration
431
- * @param {string} [options.titleArea.title='Chat'] - Title text/HTML
432
- * @param {boolean} [options.titleArea.show=false] - Whether to show title area initially
433
- * @param {'left'|'center'|'right'} [options.titleArea.align='center'] - Title alignment
434
- * @param {Object} [options.messagesArea] - Messages area configuration
435
- * @param {boolean} [options.messagesArea.alternating=true] - Alternate message colors
436
- * @param {Object} [options.inputArea] - Input area configuration
437
- * @param {boolean} [options.inputArea.show=true] - Whether to show input area initially
438
- * @param {boolean} [options.sendOnEnter=true] - Send message on Enter key
439
- * @param {boolean} [options.sendOnShiftEnter=false] - Send message on Shift+Enter
440
- * @param {string} [options.instanceClass=''] - Additional CSS class for the widget instance
441
- * @example
442
- * // Basic usage
443
- * const chat = new quikchat('#chat-container', (instance, message) => {
444
- * console.log('User sent:', message);
445
- * });
446
- *
447
- * @example
448
- * // With options
449
- * const chat = new quikchat('#chat', handleMessage, {
450
- * theme: 'quikchat-theme-dark',
451
- * titleArea: { title: 'Support Chat', show: true },
452
- * sendOnEnter: false,
453
- * sendOnShiftEnter: true
454
- * });
121
+ *
122
+ * @param string or DOM element parentElement
123
+ * @param {*} meta
455
124
  */
456
125
  function quikchat(parentElement) {
457
126
  var onSend = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
@@ -460,6 +129,7 @@ var quikchat = /*#__PURE__*/function () {
460
129
  var defaultOpts = {
461
130
  theme: 'quikchat-theme-light',
462
131
  trackHistory: true,
132
+ showTimestamps: false,
463
133
  titleArea: {
464
134
  title: "Chat",
465
135
  show: false,
@@ -467,55 +137,19 @@ var quikchat = /*#__PURE__*/function () {
467
137
  },
468
138
  messagesArea: {
469
139
  alternating: true
470
- },
471
- inputArea: {
472
- show: true
473
- },
474
- sendOnEnter: true,
475
- sendOnShiftEnter: false,
476
- instanceClass: '',
477
- virtualScrolling: true,
478
- // Default to true for better performance
479
- virtualScrollingThreshold: 500,
480
- // Lower threshold since it performs so well
481
- // i18n support
482
- lang: 'en',
483
- dir: 'ltr',
484
- // 'ltr' or 'rtl'
485
- translations: {
486
- 'en': {
487
- sendButton: 'Send',
488
- inputPlaceholder: 'Type a message...',
489
- titleDefault: 'Chat'
490
- }
491
- },
492
- // Security: content sanitizer callback
493
- sanitizer: null // null = no sanitization (backward compatible)
140
+ }
494
141
  };
495
142
  var meta = _objectSpread2(_objectSpread2({}, defaultOpts), options); // merge options with defaults
496
143
 
497
- // Merge user translations with defaults
498
- if (options.translations) {
499
- meta.translations = _objectSpread2(_objectSpread2({}, defaultOpts.translations), options.translations);
500
- }
501
144
  if (typeof parentElement === 'string') {
502
145
  parentElement = document.querySelector(parentElement);
503
146
  }
504
- //console.log(parentElement, meta);
505
147
  this._parentElement = parentElement;
506
148
  this._theme = meta.theme;
507
149
  this._onSend = onSend ? onSend : function () {}; // call back function for onSend
508
-
509
- // i18n settings
510
- this.lang = meta.lang;
511
- this.dir = meta.dir;
512
- this.translations = meta.translations;
513
- this.currentTranslations = this.translations[this.lang] || this.translations['en'];
150
+ this._messageFormatter = meta.messageFormatter || null;
151
+ this._sanitize = meta.sanitize || false;
514
152
  this._createWidget();
515
- if (meta.instanceClass) {
516
- this._chatWidget.classList.add(meta.instanceClass);
517
- }
518
-
519
153
  // title area
520
154
  if (meta.titleArea) {
521
155
  this.titleAreaSetContents(meta.titleArea.title, meta.titleArea.align);
@@ -529,176 +163,34 @@ var quikchat = /*#__PURE__*/function () {
529
163
  if (meta.messagesArea) {
530
164
  this.messagesAreaAlternateColors(meta.messagesArea.alternating);
531
165
  }
532
-
533
- // input area
534
- if (meta.inputArea) {
535
- if (meta.inputArea.show === true) this.inputAreaShow();else this.inputAreaHide();
166
+ // timestamps
167
+ if (meta.showTimestamps) {
168
+ this.messagesAreaShowTimestamps(true);
169
+ }
170
+ // direction (ltr/rtl)
171
+ if (meta.direction) {
172
+ this.setDirection(meta.direction);
536
173
  }
537
174
  // plumbing
538
175
  this._attachEventListeners();
539
- this.trackHistory = meta.trackHistory || true;
176
+ this.trackHistory = meta.trackHistory !== false;
540
177
  this._historyLimit = 10000000;
541
178
  this._history = [];
542
- this._activeTags = new Set();
543
-
544
- // send on enter / shift enter
545
- this.sendOnEnter = meta.sendOnEnter;
546
- this.sendOnShiftEnter = meta.sendOnShiftEnter;
547
-
548
- // Virtual scrolling setup
549
- this.virtualScrollingEnabled = meta.virtualScrolling;
550
- this.virtualScrollingThreshold = meta.virtualScrollingThreshold;
551
- this.virtualScroller = null;
552
-
553
- // Sanitizer setup
554
- this._sanitizer = meta.sanitizer || null;
555
-
556
- // Don't initialize virtual scrolling immediately - wait for threshold
557
- // Virtual scrolling will be initialized when message count exceeds threshold
558
179
  }
559
-
560
- /**
561
- * Initialize virtual scrolling
562
- * @private
563
- */
564
180
  return _createClass(quikchat, [{
565
- key: "_initVirtualScrolling",
566
- value: function _initVirtualScrolling() {
567
- var _this4 = this;
568
- if (this.virtualScrollingEnabled && this._messagesArea && !this.virtualScroller) {
569
- // Check if we've hit the threshold (or about to with the next message)
570
- if (this._history.length >= this.virtualScrollingThreshold - 1) {
571
- this.virtualScroller = new SimpleVirtualScroller(this._messagesArea, {
572
- itemHeight: 80,
573
- buffer: 5,
574
- sanitizer: this._sanitizer,
575
- // Pass sanitizer to virtual scroller
576
- onRenderItem: function onRenderItem(content) {
577
- // Apply alternating colors if enabled
578
- if (_this4._messagesArea.classList.contains('quikchat-messages-area-alt')) {
579
- _this4._updateMessageStyles();
580
- }
581
- }
582
- });
583
-
584
- // Migrate existing messages to virtual scroller
585
- this._migrateToVirtualScrolling();
586
- }
587
- }
588
- }
589
-
590
- /**
591
- * Migrate existing messages to virtual scrolling
592
- * @private
593
- */
594
- }, {
595
- key: "_migrateToVirtualScrolling",
596
- value: function _migrateToVirtualScrolling() {
597
- var _this5 = this;
598
- if (!this.virtualScroller) return;
599
-
600
- // Don't clear DOM here - virtual scroller will do it in _initStructure
601
-
602
- // Add all messages to virtual scroller
603
- var items = this._history.map(function (msg) {
604
- return {
605
- msgid: msg.msgid,
606
- content: msg.content,
607
- userString: msg.userString,
608
- align: msg.align,
609
- role: msg.role,
610
- visible: msg.visible,
611
- tags: msg.tags || [],
612
- scrollIntoView: false
613
- };
614
- });
615
- this.virtualScroller.addItems(items);
616
-
617
- // Force a render in case container needs time to get dimensions
618
- setTimeout(function () {
619
- if (_this5.virtualScroller) {
620
- _this5.virtualScroller._updateVisibleRange();
621
- _this5.virtualScroller._renderVisibleItems();
622
- }
623
- }, 10);
624
- }
625
- }, {
626
181
  key: "_createWidget",
627
182
  value: function _createWidget() {
628
- var sendButtonText = this.currentTranslations.sendButton || 'Send';
629
- var inputPlaceholder = this.currentTranslations.inputPlaceholder || 'Type a message...';
630
- var widgetHTML = "\n <div class=\"quikchat-base ".concat(this._theme, "\" dir=\"").concat(this.dir, "\" lang=\"").concat(this.lang, "\" role=\"region\" aria-label=\"Chat widget\">\n <div class=\"quikchat-title-area\" role=\"heading\" aria-level=\"2\">\n <span style=\"font-size: 1.5em; font-weight: 600;\">Title Area</span>\n </div>\n <div class=\"quikchat-messages-area\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div>\n <div class=\"quikchat-input-area\" role=\"form\" aria-label=\"Message input\">\n <textarea class=\"quikchat-input-textbox\" \n placeholder=\"").concat(inputPlaceholder, "\"\n aria-label=\"Type your message\"\n autocomplete=\"off\"\n autocapitalize=\"sentences\"></textarea>\n <button class=\"quikchat-input-send-btn\" \n aria-label=\"Send message\"\n type=\"button\">").concat(sendButtonText, "</button>\n </div>\n </div>\n ");
183
+ var widgetHTML = "\n <div class=\"quikchat-base ".concat(this.theme, "\">\n <div class=\"quikchat-title-area\"></div>\n <div class=\"quikchat-messages-wrapper\"><div class=\"quikchat-messages-area\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div><button class=\"quikchat-scroll-bottom\" aria-label=\"Scroll to bottom\"></button></div>\n <div class=\"quikchat-input-area\">\n <textarea class=\"quikchat-input-textbox\" rows=\"1\" aria-label=\"Type a message\"></textarea>\n <button class=\"quikchat-input-send-btn\">Send</button>\n </div>\n </div>\n ");
631
184
  this._parentElement.innerHTML = widgetHTML;
632
185
  this._chatWidget = this._parentElement.querySelector('.quikchat-base');
633
186
  this._titleArea = this._chatWidget.querySelector('.quikchat-title-area');
187
+ this._messagesWrapper = this._chatWidget.querySelector('.quikchat-messages-wrapper');
634
188
  this._messagesArea = this._chatWidget.querySelector('.quikchat-messages-area');
189
+ this._scrollBottomBtn = this._messagesWrapper.querySelector('.quikchat-scroll-bottom');
635
190
  this._inputArea = this._chatWidget.querySelector('.quikchat-input-area');
636
191
  this._textEntry = this._inputArea.querySelector('.quikchat-input-textbox');
637
192
  this._sendButton = this._inputArea.querySelector('.quikchat-input-send-btn');
638
193
  this.msgid = 0;
639
-
640
- // Add mobile viewport handling
641
- this._setupMobileSupport();
642
- }
643
-
644
- /**
645
- * Setup mobile support - prevent zoom on input focus and handle virtual keyboard
646
- * @private
647
- */
648
- }, {
649
- key: "_setupMobileSupport",
650
- value: function _setupMobileSupport() {
651
- var _this6 = this;
652
- // Prevent zoom on input focus for mobile
653
- var meta = document.querySelector('meta[name="viewport"]');
654
- if (!meta) {
655
- meta = document.createElement('meta');
656
- meta.name = 'viewport';
657
- document.head.appendChild(meta);
658
- }
659
-
660
- // Store original content
661
- this._originalViewport = meta.content;
662
-
663
- // Prevent zoom on focus
664
- this._textEntry.addEventListener('focus', function () {
665
- if (window.innerWidth <= 768) {
666
- // Mobile device width
667
- meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0';
668
- }
669
- });
670
- this._textEntry.addEventListener('blur', function () {
671
- if (_this6._originalViewport) {
672
- meta.content = _this6._originalViewport;
673
- } else {
674
- meta.content = 'width=device-width, initial-scale=1.0';
675
- }
676
- });
677
-
678
- // Handle virtual keyboard resize
679
- if ('visualViewport' in window) {
680
- window.visualViewport.addEventListener('resize', function () {
681
- _this6._handleVirtualKeyboard();
682
- });
683
- }
684
- }
685
-
686
- /**
687
- * Handle virtual keyboard appearance/disappearance
688
- * @private
689
- */
690
- }, {
691
- key: "_handleVirtualKeyboard",
692
- value: function _handleVirtualKeyboard() {
693
- // Adjust layout when virtual keyboard appears
694
- var keyboardHeight = window.innerHeight - window.visualViewport.height;
695
- if (keyboardHeight > 0) {
696
- // Keyboard is visible - adjust chat widget height
697
- this._chatWidget.style.paddingBottom = "".concat(keyboardHeight, "px");
698
- } else {
699
- // Keyboard hidden - restore original padding
700
- this._chatWidget.style.paddingBottom = '';
701
- }
702
194
  }
703
195
 
704
196
  /**
@@ -707,40 +199,55 @@ var quikchat = /*#__PURE__*/function () {
707
199
  }, {
708
200
  key: "_attachEventListeners",
709
201
  value: function _attachEventListeners() {
710
- var _this7 = this;
711
- this._sendButton.addEventListener('click', function (event) {
712
- event.preventDefault();
713
- _this7._onSend(_this7, _this7._textEntry.value.trim());
714
- });
715
- window.addEventListener('resize', function () {
716
- return _this7._handleContainerResize();
717
- });
718
- this._chatWidget.addEventListener('resize', function () {
719
- return _this7._handleContainerResize();
202
+ var _this = this;
203
+ this._sendButton.addEventListener('click', function () {
204
+ var text = _this._textEntry.value.trim();
205
+ if (text === '') return;
206
+ _this._onSend(_this, text);
720
207
  });
721
208
  this._textEntry.addEventListener('keydown', function (event) {
722
- // Check if Shift + Enter is pressed then we just do carraige
209
+ // Check if Shift + Enter is pressed
723
210
  if (event.shiftKey && event.keyCode === 13) {
724
- // Prevent default behavior (adding new line)
725
- if (_this7.sendOnShiftEnter) {
726
- event.preventDefault();
727
- _this7._onSend(_this7, _this7._textEntry.value.trim());
728
- }
729
- } else if (event.keyCode === 13) {
730
- // Enter but not Shift + Enter
731
- if (_this7.sendOnEnter) {
732
- event.preventDefault();
733
- _this7._onSend(_this7, _this7._textEntry.value.trim());
734
- }
211
+ event.preventDefault();
212
+ var text = _this._textEntry.value.trim();
213
+ if (text === '') return;
214
+ _this._onSend(_this, text);
735
215
  }
736
216
  });
217
+
218
+ // Auto-grow textarea
219
+ this._textEntry.addEventListener('input', function () {
220
+ return _this._autoGrowTextarea();
221
+ });
737
222
  this._messagesArea.addEventListener('scroll', function () {
738
- var _this7$_messagesArea = _this7._messagesArea,
739
- scrollTop = _this7$_messagesArea.scrollTop,
740
- scrollHeight = _this7$_messagesArea.scrollHeight,
741
- clientHeight = _this7$_messagesArea.clientHeight;
742
- _this7.userScrolledUp = scrollTop + clientHeight < scrollHeight;
223
+ var _this$_messagesArea = _this._messagesArea,
224
+ scrollTop = _this$_messagesArea.scrollTop,
225
+ scrollHeight = _this$_messagesArea.scrollHeight,
226
+ clientHeight = _this$_messagesArea.clientHeight;
227
+ _this.userScrolledUp = scrollTop + clientHeight < scrollHeight - 1;
228
+ _this._updateScrollBottomBtn();
229
+ });
230
+
231
+ // Scroll-to-bottom button
232
+ this._scrollBottomBtn.addEventListener('click', function () {
233
+ return _this.scrollToBottom();
234
+ });
235
+
236
+ // Ctrl+End to scroll to bottom
237
+ this._chatWidget.addEventListener('keydown', function (event) {
238
+ if (event.ctrlKey && event.key === 'End') {
239
+ event.preventDefault();
240
+ _this.scrollToBottom();
241
+ }
743
242
  });
243
+
244
+ // Use ResizeObserver to detect parent container resize
245
+ if (typeof ResizeObserver !== 'undefined') {
246
+ this._resizeObserver = new ResizeObserver(function () {
247
+ return _this._handleContainerResize();
248
+ });
249
+ this._resizeObserver.observe(this._parentElement);
250
+ }
744
251
  }
745
252
 
746
253
  // set the onSend function callback.
@@ -755,54 +262,16 @@ var quikchat = /*#__PURE__*/function () {
755
262
  value: function setCallbackonMessageAdded(callback) {
756
263
  this._onMessageAdded = callback;
757
264
  }
758
-
759
- /**
760
- * Sets the callback function for when content is appended to a message
761
- * @param {Function} callback - Function to call when content is appended
762
- * @param {quikchat} callback.instance - The QuikChat instance
763
- * @param {number} callback.msgId - The ID of the message being appended to
764
- * @param {string} callback.content - The content being appended
765
- * @since 1.1.15
766
- * @example
767
- * chat.setCallbackonMessageAppend((instance, msgId, content) => {
768
- * console.log(`Appended "${content}" to message ${msgId}`);
769
- * });
770
- */
771
265
  }, {
772
266
  key: "setCallbackonMessageAppend",
773
267
  value: function setCallbackonMessageAppend(callback) {
774
268
  this._onMessageAppend = callback;
775
269
  }
776
-
777
- /**
778
- * Sets the callback function for when a message's content is replaced
779
- * @param {Function} callback - Function to call when content is replaced
780
- * @param {quikchat} callback.instance - The QuikChat instance
781
- * @param {number} callback.msgId - The ID of the message being replaced
782
- * @param {string} callback.content - The new content
783
- * @since 1.1.15
784
- * @example
785
- * chat.setCallbackonMessageReplace((instance, msgId, content) => {
786
- * console.log(`Message ${msgId} replaced with: ${content}`);
787
- * });
788
- */
789
270
  }, {
790
271
  key: "setCallbackonMessageReplace",
791
272
  value: function setCallbackonMessageReplace(callback) {
792
273
  this._onMessageReplace = callback;
793
274
  }
794
-
795
- /**
796
- * Sets the callback function for when a message is deleted
797
- * @param {Function} callback - Function to call when a message is deleted
798
- * @param {quikchat} callback.instance - The QuikChat instance
799
- * @param {number} callback.msgId - The ID of the deleted message
800
- * @since 1.1.15
801
- * @example
802
- * chat.setCallbackonMessageDelete((instance, msgId) => {
803
- * console.log(`Message ${msgId} was deleted`);
804
- * });
805
- */
806
275
  }, {
807
276
  key: "setCallbackonMessageDelete",
808
277
  value: function setCallbackonMessageDelete(callback) {
@@ -810,58 +279,32 @@ var quikchat = /*#__PURE__*/function () {
810
279
  }
811
280
 
812
281
  // Public methods
813
- /**
814
- * Toggles the visibility of the title area
815
- * @returns {void}
816
- */
817
282
  }, {
818
283
  key: "titleAreaToggle",
819
284
  value: function titleAreaToggle() {
820
- this._titleArea.style.display === 'none' ? this.titleAreaShow() : this.titleAreaHide();
285
+ if (this._titleArea.style.display === 'none') {
286
+ this.titleAreaShow();
287
+ } else {
288
+ this.titleAreaHide();
289
+ }
821
290
  }
822
-
823
- /**
824
- * Shows the title area
825
- * @returns {void}
826
- */
827
291
  }, {
828
292
  key: "titleAreaShow",
829
293
  value: function titleAreaShow() {
830
294
  this._titleArea.style.display = '';
831
- this._adjustMessagesAreaHeight();
832
295
  }
833
-
834
- /**
835
- * Hides the title area
836
- * @returns {void}
837
- */
838
296
  }, {
839
297
  key: "titleAreaHide",
840
298
  value: function titleAreaHide() {
841
299
  this._titleArea.style.display = 'none';
842
- this._adjustMessagesAreaHeight();
843
300
  }
844
-
845
- /**
846
- * Sets the contents of the title area
847
- * @param {string} title - HTML content to display in the title area
848
- * @param {'left'|'center'|'right'} [align='center'] - Text alignment
849
- * @returns {void}
850
- * @example
851
- * chat.titleAreaSetContents('<h2>Support Chat</h2>', 'center');
852
- */
853
301
  }, {
854
302
  key: "titleAreaSetContents",
855
303
  value: function titleAreaSetContents(title) {
856
304
  var align = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center';
857
- this._titleArea.innerHTML = this._sanitizeContent(title);
305
+ this._titleArea.innerHTML = title;
858
306
  this._titleArea.style.textAlign = align;
859
307
  }
860
-
861
- /**
862
- * Gets the current contents of the title area
863
- * @returns {string} The HTML content of the title area
864
- */
865
308
  }, {
866
309
  key: "titleAreaGetContents",
867
310
  value: function titleAreaGetContents() {
@@ -870,48 +313,133 @@ var quikchat = /*#__PURE__*/function () {
870
313
  }, {
871
314
  key: "inputAreaToggle",
872
315
  value: function inputAreaToggle() {
873
- this._inputArea.classList.toggle('hidden');
874
- this._inputArea.style.display === 'none' ? this.inputAreaShow() : this.inputAreaHide();
316
+ if (this._inputArea.style.display === 'none') {
317
+ this.inputAreaShow();
318
+ } else {
319
+ this.inputAreaHide();
320
+ }
875
321
  }
876
322
  }, {
877
323
  key: "inputAreaShow",
878
324
  value: function inputAreaShow() {
879
325
  this._inputArea.style.display = '';
880
- this._adjustMessagesAreaHeight();
881
326
  }
882
327
  }, {
883
328
  key: "inputAreaHide",
884
329
  value: function inputAreaHide() {
885
330
  this._inputArea.style.display = 'none';
886
- this._adjustMessagesAreaHeight();
887
331
  }
888
332
  }, {
889
- key: "_adjustMessagesAreaHeight",
890
- value: function _adjustMessagesAreaHeight() {
891
- var hiddenElements = _toConsumableArray(this._chatWidget.children).filter(function (child) {
892
- return child.classList.contains('hidden');
893
- });
894
- var totalHiddenHeight = hiddenElements.reduce(function (sum, child) {
895
- return sum + child.offsetHeight;
896
- }, 0);
897
- var containerHeight = this._chatWidget.offsetHeight;
898
- this._messagesArea.style.height = "calc(100% - ".concat(containerHeight - totalHiddenHeight, "px)");
333
+ key: "inputAreaSetEnabled",
334
+ value: function inputAreaSetEnabled(enabled) {
335
+ this._textEntry.disabled = !enabled;
336
+ this._sendButton.disabled = !enabled;
337
+ }
338
+ }, {
339
+ key: "inputAreaSetButtonText",
340
+ value: function inputAreaSetButtonText(text) {
341
+ this._sendButton.textContent = text;
342
+ }
343
+ }, {
344
+ key: "inputAreaGetButtonText",
345
+ value: function inputAreaGetButtonText() {
346
+ return this._sendButton.textContent;
347
+ }
348
+ }, {
349
+ key: "setDirection",
350
+ value: function setDirection(dir) {
351
+ var d = dir === 'rtl' ? 'rtl' : 'ltr';
352
+ this._chatWidget.setAttribute('dir', d);
353
+ if (d === 'rtl') {
354
+ this._chatWidget.classList.add('quikchat-rtl');
355
+ } else {
356
+ this._chatWidget.classList.remove('quikchat-rtl');
357
+ }
358
+ }
359
+ }, {
360
+ key: "getDirection",
361
+ value: function getDirection() {
362
+ return this._chatWidget.getAttribute('dir') || 'ltr';
899
363
  }
900
364
  }, {
901
365
  key: "_handleContainerResize",
902
366
  value: function _handleContainerResize() {
903
- this._adjustMessagesAreaHeight();
904
- this._adjustSendButtonWidth();
905
- return true;
367
+ // Layout is handled by CSS flexbox — no JS height calculation needed.
368
+ // This hook exists for future use or custom resize callbacks.
906
369
  }
907
370
  }, {
908
- key: "_adjustSendButtonWidth",
909
- value: function _adjustSendButtonWidth() {
910
- var sendButtonText = this._sendButton.textContent.trim();
911
- var fontSize = parseFloat(getComputedStyle(this._sendButton).fontSize);
912
- var minWidth = fontSize * sendButtonText.length + 16;
913
- this._sendButton.style.minWidth = "".concat(minWidth, "px");
914
- return true;
371
+ key: "scrollToBottom",
372
+ value: function scrollToBottom() {
373
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
374
+ this.userScrolledUp = false;
375
+ this._updateScrollBottomBtn();
376
+ }
377
+ }, {
378
+ key: "_updateScrollBottomBtn",
379
+ value: function _updateScrollBottomBtn() {
380
+ if (this.userScrolledUp) {
381
+ this._scrollBottomBtn.classList.add('quikchat-scroll-bottom-visible');
382
+ } else {
383
+ this._scrollBottomBtn.classList.remove('quikchat-scroll-bottom-visible');
384
+ }
385
+ }
386
+ }, {
387
+ key: "_autoGrowTextarea",
388
+ value: function _autoGrowTextarea() {
389
+ var el = this._textEntry;
390
+ el.style.height = 'auto';
391
+ var maxHeight = parseInt(getComputedStyle(el).getPropertyValue('--quikchat-input-max-height')) || 120;
392
+ el.style.height = Math.min(el.scrollHeight, maxHeight) + 'px';
393
+ el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';
394
+ }
395
+ }, {
396
+ key: "_formatTimestamp",
397
+ value: function _formatTimestamp(isoString) {
398
+ var d = new Date(isoString);
399
+ var h = d.getHours();
400
+ var m = String(d.getMinutes()).padStart(2, '0');
401
+ var ampm = h >= 12 ? 'PM' : 'AM';
402
+ var h12 = h % 12 || 12;
403
+ return h12 + ':' + m + ' ' + ampm;
404
+ }
405
+ }, {
406
+ key: "messagesAreaShowTimestamps",
407
+ value: function messagesAreaShowTimestamps(show) {
408
+ if (show) {
409
+ this._messagesArea.classList.add('quikchat-show-timestamps');
410
+ } else {
411
+ this._messagesArea.classList.remove('quikchat-show-timestamps');
412
+ }
413
+ }
414
+ }, {
415
+ key: "messagesAreaShowTimestampsGet",
416
+ value: function messagesAreaShowTimestampsGet() {
417
+ return this._messagesArea.classList.contains('quikchat-show-timestamps');
418
+ }
419
+ }, {
420
+ key: "messagesAreaShowTimestampsToggle",
421
+ value: function messagesAreaShowTimestampsToggle() {
422
+ this._messagesArea.classList.toggle('quikchat-show-timestamps');
423
+ }
424
+ }, {
425
+ key: "_escapeHTML",
426
+ value: function _escapeHTML(str) {
427
+ var div = document.createElement('div');
428
+ div.textContent = str;
429
+ return div.innerHTML;
430
+ }
431
+ }, {
432
+ key: "_processContent",
433
+ value: function _processContent(content) {
434
+ if (this._sanitize === true) {
435
+ content = this._escapeHTML(content);
436
+ } else if (typeof this._sanitize === 'function') {
437
+ content = this._sanitize(content);
438
+ }
439
+ if (this._messageFormatter) {
440
+ content = this._messageFormatter(content);
441
+ }
442
+ return content;
915
443
  }
916
444
 
917
445
  //messagesArea functions
@@ -936,151 +464,67 @@ var quikchat = /*#__PURE__*/function () {
936
464
  value: function messagesAreaAlternateColorsGet() {
937
465
  return this._messagesArea.classList.contains('quikchat-messages-area-alt');
938
466
  }
939
- /**
940
- * Adds a new message to the chat with full configuration options
941
- * @param {Object} input - Message configuration object
942
- * @param {string} [input.content=''] - Message content (HTML allowed)
943
- * @param {string} [input.userString='user'] - Display name for the message sender
944
- * @param {'left'|'right'|'center'} [input.align='right'] - Message alignment
945
- * @param {string} [input.role='user'] - Role identifier (user, assistant, system)
946
- * @param {number} [input.userID=-1] - User ID for the message
947
- * @param {string|false} [input.timestamp=false] - ISO timestamp or false for auto
948
- * @param {string|false} [input.updatedtime=false] - Last updated timestamp
949
- * @param {boolean|'smart'} [input.scrollIntoView=true] - Scroll behavior (true/false/'smart')
950
- * @param {boolean} [input.visible=true] - Whether message is initially visible
951
- * @param {string[]} [input.tags=[]] - Tags for message categorization
952
- * @returns {number} Message ID for the newly added message
953
- * @example
954
- * const msgId = chat.messageAddFull({
955
- * content: 'Hello!',
956
- * userString: 'Bot',
957
- * align: 'left',
958
- * scrollIntoView: 'smart',
959
- * tags: ['greeting']
960
- * });
961
- */
467
+ // message functions
962
468
  }, {
963
469
  key: "messageAddFull",
964
470
  value: function messageAddFull() {
965
- var _this8 = this;
966
471
  var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
967
472
  content: "",
968
473
  userString: "user",
969
474
  align: "right",
970
475
  role: "user",
971
- userID: -1,
972
- timestamp: false,
973
- updatedtime: false,
974
- scrollIntoView: true,
975
- visible: true,
976
- tags: []
476
+ userID: -1
977
477
  };
978
478
  var msgid = this.msgid;
479
+ var messageDiv = document.createElement('div');
480
+ var msgidClass = 'quikchat-msgid-' + String(msgid).padStart(10, '0');
481
+ messageDiv.classList.add('quikchat-message', msgidClass);
482
+ messageDiv.classList.add('quikchat-role-' + (input.role || 'user'));
483
+ messageDiv.classList.add('quikchat-align-' + (input.align || 'right'));
979
484
  this.msgid++;
980
- var messageDiv = null; // Initialize messageDiv to null
485
+ messageDiv.classList.add(this._messagesArea.children.length % 2 === 1 ? 'quikchat-message-1' : 'quikchat-message-2');
981
486
 
982
- // Check if we should initialize virtual scrolling
983
- if (this.virtualScrollingEnabled && !this.virtualScroller && this._history.length >= this.virtualScrollingThreshold - 1) {
984
- this._initVirtualScrolling();
487
+ // Visibility: default true, hidden messages get display:none
488
+ var visible = input.visible !== false;
489
+ if (!visible) {
490
+ messageDiv.style.display = 'none';
985
491
  }
986
492
 
987
- // If virtual scrolling is enabled, use the virtual scroller
988
- if (this.virtualScroller) {
989
- var messageData = {
990
- msgid: msgid,
991
- content: input.content,
992
- userString: input.userString,
993
- align: input.align,
994
- role: input.role,
995
- visible: input.visible !== undefined ? input.visible : true,
996
- tags: input.tags || [],
997
- scrollIntoView: input.scrollIntoView
998
- };
999
-
1000
- // Add tags to active tags set
1001
- if (Array.isArray(input.tags)) {
1002
- input.tags.forEach(function (tag) {
1003
- if (typeof tag === 'string' && /^[a-zA-Z0-9-]+$/.test(tag)) {
1004
- _this8._activeTags.add(tag);
1005
- }
1006
- });
1007
- }
1008
-
1009
- // Add to virtual scroller
1010
- this.virtualScroller.addItem(messageData);
1011
-
1012
- // Clear text entry
1013
- this._textEntry.value = '';
1014
- this._adjustMessagesAreaHeight();
1015
- } else {
1016
- // Original DOM-based implementation
1017
- messageDiv = document.createElement('div');
1018
- var msgidClass = 'quikchat-msgid-' + String(msgid).padStart(10, '0');
1019
- 'quikchat-userid-' + String(input.userString).padStart(10, '0'); // hash this..
1020
- messageDiv.classList.add('quikchat-message', msgidClass, 'quikchat-structure');
1021
- messageDiv.setAttribute('role', 'article');
1022
- messageDiv.setAttribute('aria-label', "Message from ".concat(input.userString || 'user'));
1023
- if (Array.isArray(input.tags)) {
1024
- input.tags.forEach(function (tag) {
1025
- if (typeof tag === 'string' && /^[a-zA-Z0-9-]+$/.test(tag)) {
1026
- messageDiv.classList.add("quikchat-tag-".concat(tag));
1027
- _this8._activeTags.add(tag);
1028
- }
1029
- });
1030
- }
1031
- var userDiv = document.createElement('div');
1032
- userDiv.innerHTML = this._sanitizeContent(input.userString);
1033
- userDiv.classList.add('quikchat-user-label');
1034
- userDiv.style.textAlign = input.align;
1035
- var contentDiv = document.createElement('div');
1036
- contentDiv.classList.add('quikchat-message-content');
1037
-
1038
- // Determine text alignment for right-aligned messages
1039
- if (input.align === "right") {
1040
- var isMultiLine = input.content.includes("\n");
1041
- var isLong = input.content.length > 50; // Adjust length threshold
1042
-
1043
- if (isMultiLine || isLong) {
1044
- contentDiv.classList.add("quikchat-right-multiline");
1045
- } else {
1046
- contentDiv.classList.add("quikchat-right-singleline");
1047
- }
1048
- }
1049
- contentDiv.innerHTML = this._sanitizeContent(input.content);
1050
- messageDiv.appendChild(userDiv);
1051
- messageDiv.appendChild(contentDiv);
1052
- this._messagesArea.appendChild(messageDiv);
1053
- if (input.visible === false) {
1054
- messageDiv.style.display = 'none';
1055
- }
1056
-
1057
- // Handle scroll behavior based on scrollIntoView parameter
1058
- // 'smart' = only scroll if near bottom, true = always scroll, false = never scroll
1059
- if (input.scrollIntoView === true) {
1060
- this.messageScrollToBottom();
1061
- } else if (input.scrollIntoView === 'smart' && !this.userScrolledUp) {
1062
- this.messageScrollToBottom();
1063
- }
1064
- // If scrollIntoView is false, don't scroll at all
493
+ // Tags: array of strings for group-based visibility control
494
+ var tags = Array.isArray(input.tags) ? input.tags.slice() : [];
495
+ var userDiv = document.createElement('div');
496
+ userDiv.classList.add('quikchat-user-label');
497
+ userDiv.style.textAlign = input.align;
498
+ userDiv.innerHTML = input.userString;
499
+ var contentDiv = document.createElement('div');
500
+ contentDiv.classList.add('quikchat-message-content');
501
+ contentDiv.style.textAlign = input.align;
502
+ contentDiv.innerHTML = this._processContent(input.content);
503
+ var timestamp = new Date().toISOString();
504
+ var timestampSpan = document.createElement('span');
505
+ timestampSpan.classList.add('quikchat-timestamp');
506
+ timestampSpan.textContent = this._formatTimestamp(timestamp);
507
+ messageDiv.appendChild(userDiv);
508
+ messageDiv.appendChild(contentDiv);
509
+ messageDiv.appendChild(timestampSpan);
510
+ this._messagesArea.appendChild(messageDiv);
1065
511
 
1066
- this._textEntry.value = '';
1067
- this._adjustMessagesAreaHeight();
1068
- this._handleShortLongMessageCSS(messageDiv, input.align); // Handle CSS for short/long messages
1069
- this._updateMessageStyles();
512
+ // Scroll to the last message only if the user is not actively scrolling up
513
+ if (!this.userScrolledUp) {
514
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
1070
515
  }
1071
-
1072
- // Add timestamp now, unless it is passed in
1073
- var timestamp = input.timestamp ? input.timestamp : new Date().toISOString();
1074
- var updatedtime = input.updatedtime ? input.updatedtime : timestamp;
1075
- var visible = input.visible !== undefined ? input.visible : true;
516
+ this._textEntry.value = '';
517
+ this._autoGrowTextarea();
518
+ var updatedtime = timestamp;
1076
519
  if (this.trackHistory) {
1077
520
  this._history.push(_objectSpread2(_objectSpread2({
1078
521
  msgid: msgid
1079
522
  }, input), {}, {
1080
523
  visible: visible,
524
+ tags: tags,
1081
525
  timestamp: timestamp,
1082
526
  updatedtime: updatedtime,
1083
- messageDiv: messageDiv || null
527
+ messageDiv: messageDiv
1084
528
  }));
1085
529
  if (this._history.length > this._historyLimit) {
1086
530
  this._history.shift();
@@ -1091,24 +535,6 @@ var quikchat = /*#__PURE__*/function () {
1091
535
  }
1092
536
  return msgid;
1093
537
  }
1094
-
1095
- /**
1096
- * Adds a new message to the chat (simplified version of messageAddFull)
1097
- * @param {string} [content=''] - Message content (HTML allowed)
1098
- * @param {string} [userString='user'] - Display name for the message sender
1099
- * @param {'left'|'right'|'center'} [align='right'] - Message alignment
1100
- * @param {string} [role='user'] - Role identifier (user, assistant, system)
1101
- * @param {boolean|'smart'} [scrollIntoView=true] - Scroll behavior
1102
- * @param {boolean} [visible=true] - Whether message is initially visible
1103
- * @param {string[]} [tags=[]] - Tags for message categorization
1104
- * @returns {number} Message ID for the newly added message
1105
- * @example
1106
- * // Simple message
1107
- * chat.messageAddNew('Hello!', 'User', 'right');
1108
- *
1109
- * // Bot message with smart scroll
1110
- * chat.messageAddNew('Hi there!', 'Bot', 'left', 'assistant', 'smart');
1111
- */
1112
538
  }, {
1113
539
  key: "messageAddNew",
1114
540
  value: function messageAddNew() {
@@ -1116,115 +542,125 @@ var quikchat = /*#__PURE__*/function () {
1116
542
  var userString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "user";
1117
543
  var align = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "right";
1118
544
  var role = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "user";
1119
- var scrollIntoView = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
1120
- var visible = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
1121
- var tags = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : [];
1122
- var retvalue = this.messageAddFull({
545
+ return this.messageAddFull({
1123
546
  content: content,
1124
547
  userString: userString,
1125
548
  align: align,
1126
- role: role,
1127
- scrollIntoView: scrollIntoView,
1128
- visible: visible,
1129
- tags: tags
549
+ role: role
1130
550
  });
1131
- // this.messageScrollToBottom();
1132
- return retvalue;
1133
551
  }
1134
-
1135
- /**
1136
- * Removes a message from the chat by its ID
1137
- * @param {number} n - Message ID to remove
1138
- * @returns {boolean} True if message was removed, false if not found
1139
- * @example
1140
- * const success = chat.messageRemove(5);
1141
- */
1142
552
  }, {
1143
553
  key: "messageRemove",
1144
554
  value: function messageRemove(n) {
1145
- // use css selector to remove the message
1146
- var sucess = false;
555
+ var success = false;
1147
556
  try {
1148
557
  this._messagesArea.removeChild(this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))));
1149
- sucess = true;
1150
- } catch (error) {
1151
- console.log("{String(n)} : Message ID not found");
558
+ success = true;
559
+ } catch (_error) {
560
+ // Message ID not found
1152
561
  }
1153
- if (sucess) {
1154
- // slow way to remove from history
1155
- //this._history = this._history.filter((item) => item.msgid !== n); // todo make this more efficient
1156
-
1157
- // better way to delete this from history
562
+ if (success) {
1158
563
  this._history.splice(this._history.findIndex(function (item) {
1159
564
  return item.msgid === n;
1160
565
  }), 1);
1161
-
1162
- // Call the onMessageDelete callback if it exists
1163
566
  if (this._onMessageDelete) {
1164
567
  this._onMessageDelete(this, n);
1165
568
  }
1166
569
  }
1167
- return sucess;
570
+ return success;
1168
571
  }
1169
572
  /* returns the message html object from the DOM
1170
573
  */
1171
- /**
1172
- * Gets the DOM element for a message by its ID
1173
- * @param {number} n - Message ID
1174
- * @returns {HTMLElement|null} The message DOM element or null if not found
1175
- */
1176
574
  }, {
1177
575
  key: "messageGetDOMObject",
1178
576
  value: function messageGetDOMObject(n) {
1179
577
  var msg = null;
1180
- // now use css selector to get the message
1181
578
  try {
1182
579
  msg = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
1183
- } catch (error) {
1184
- console.log("{String(n)} : Message ID not found");
580
+ } catch (_error) {
581
+ // Message ID not found
1185
582
  }
1186
583
  return msg;
1187
584
  }
1188
585
  /* returns the message content only
1189
586
  */
1190
- /**
1191
- * Gets the content of a message by its ID
1192
- * @param {number} n - Message ID
1193
- * @returns {string} The message content or empty string if not found
1194
- */
1195
587
  }, {
1196
588
  key: "messageGetContent",
1197
589
  value: function messageGetContent(n) {
1198
590
  var content = "";
1199
- // now use css selector to get the message
1200
591
  try {
1201
- // get from history..
1202
592
  content = this._history.filter(function (item) {
1203
593
  return item.msgid === n;
1204
594
  })[0].content;
1205
- //content = this._messagesArea.querySelector(`.quikchat-msgid-${String(n).padStart(10, '0')}`).lastChild.textContent;
1206
- } catch (error) {
1207
- console.log("{String(n)} : Message ID not found");
595
+ } catch (_error) {
596
+ // Message ID not found
1208
597
  }
1209
598
  return content;
1210
599
  }
1211
-
1212
- /* returns the DOM Content element of a given message
1213
- */
1214
600
  }, {
1215
- key: "messageGetContentDOMElement",
1216
- value: function messageGetContentDOMElement(n) {
1217
- var contentElement = null;
1218
- // now use css selector to get the message
601
+ key: "messageSetVisible",
602
+ value: function messageSetVisible(n, visible) {
603
+ var msgEl = this.messageGetDOMObject(n);
604
+ if (!msgEl) return false;
605
+ msgEl.style.display = visible ? '' : 'none';
606
+ var item = this._history.find(function (entry) {
607
+ return entry.msgid === n;
608
+ });
609
+ if (item) item.visible = visible;
610
+ return true;
611
+ }
612
+ }, {
613
+ key: "messageGetVisible",
614
+ value: function messageGetVisible(n) {
615
+ var item = this._history.find(function (entry) {
616
+ return entry.msgid === n;
617
+ });
618
+ return item ? item.visible !== false : false;
619
+ }
620
+ }, {
621
+ key: "messageToggleVisible",
622
+ value: function messageToggleVisible(n) {
623
+ var current = this.messageGetVisible(n);
624
+ return this.messageSetVisible(n, !current);
625
+ }
626
+ }, {
627
+ key: "messageSetVisibleByTag",
628
+ value: function messageSetVisibleByTag(tag, visible) {
629
+ var count = 0;
630
+ var _iterator = _createForOfIteratorHelper(this._history),
631
+ _step;
1219
632
  try {
1220
- //contentElement = this._messagesArea.querySelector(`.quikchat-msgid-${String(n).padStart(10, '0')}`).lastChild;
1221
- contentElement = this._history.filter(function (item) {
1222
- return item.msgid === n;
1223
- })[0].messageDiv.lastChild;
1224
- } catch (error) {
1225
- console.log("{String(n)} : Message ID not found");
633
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
634
+ var item = _step.value;
635
+ if (item.tags && item.tags.includes(tag)) {
636
+ this.messageSetVisible(item.msgid, visible);
637
+ count++;
638
+ }
639
+ }
640
+ } catch (err) {
641
+ _iterator.e(err);
642
+ } finally {
643
+ _iterator.f();
1226
644
  }
1227
- return contentElement;
645
+ return count;
646
+ }
647
+ }, {
648
+ key: "messageGetTags",
649
+ value: function messageGetTags(n) {
650
+ var item = this._history.find(function (entry) {
651
+ return entry.msgid === n;
652
+ });
653
+ return item && item.tags ? item.tags.slice() : [];
654
+ }
655
+ }, {
656
+ key: "messageSetTags",
657
+ value: function messageSetTags(n, tags) {
658
+ var item = this._history.find(function (entry) {
659
+ return entry.msgid === n;
660
+ });
661
+ if (!item) return false;
662
+ item.tags = Array.isArray(tags) ? tags.slice() : [];
663
+ return true;
1228
664
  }
1229
665
 
1230
666
  /* append message to the message content
@@ -1234,41 +670,23 @@ var quikchat = /*#__PURE__*/function () {
1234
670
  value: function messageAppendContent(n, content) {
1235
671
  var success = false;
1236
672
  try {
1237
- // Update history first
1238
- var item = this._history.filter(function (item) {
1239
- return item.msgid === n;
673
+ var msgEl = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
674
+ var item = this._history.filter(function (entry) {
675
+ return entry.msgid === n;
1240
676
  })[0];
1241
677
  item.content += content;
1242
678
  item.updatedtime = new Date().toISOString();
1243
-
1244
- // If virtual scrolling is active, update the virtual scroller
1245
- if (this.virtualScroller) {
1246
- // Find the item index in virtual scroller
1247
- var index = this.virtualScroller.items.findIndex(function (item) {
1248
- return item.msgid === n;
1249
- });
1250
- if (index >= 0) {
1251
- this.virtualScroller.items[index].content += content; // Don't double-sanitize, it's done on render
1252
- // Re-render if the item is currently visible
1253
- this.virtualScroller.updateItem(index, {
1254
- content: this.virtualScroller.items[index].content
1255
- });
1256
- }
1257
- } else {
1258
- // Regular DOM manipulation
1259
- this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))).lastChild.innerHTML += this._sanitizeContent(content);
1260
- }
679
+ msgEl.querySelector('.quikchat-message-content').innerHTML = this._processContent(item.content);
680
+ msgEl.classList.remove('quikchat-typing');
1261
681
  success = true;
1262
-
1263
- // Call the onMessageAppend callback if it exists
682
+ if (!this.userScrolledUp) {
683
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
684
+ }
1264
685
  if (this._onMessageAppend) {
1265
686
  this._onMessageAppend(this, n, content);
1266
687
  }
1267
-
1268
- // Don't auto-scroll on append - let user control this
1269
- // Users can call messageScrollToBottom() if they want to scroll
1270
- } catch (error) {
1271
- console.log("".concat(String(n), " : Message ID not found"));
688
+ } catch (_error) {
689
+ // Message ID not found
1272
690
  }
1273
691
  return success;
1274
692
  }
@@ -1280,405 +698,80 @@ var quikchat = /*#__PURE__*/function () {
1280
698
  value: function messageReplaceContent(n, content) {
1281
699
  var success = false;
1282
700
  try {
1283
- // Update history first
1284
- var item = this._history.filter(function (item) {
1285
- return item.msgid === n;
701
+ var msgEl = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
702
+ var item = this._history.filter(function (entry) {
703
+ return entry.msgid === n;
1286
704
  })[0];
1287
705
  item.content = content;
1288
706
  item.updatedtime = new Date().toISOString();
1289
-
1290
- // If virtual scrolling is active, update the virtual scroller
1291
- if (this.virtualScroller) {
1292
- // Find the item index in virtual scroller
1293
- var index = this.virtualScroller.items.findIndex(function (item) {
1294
- return item.msgid === n;
1295
- });
1296
- if (index >= 0) {
1297
- this.virtualScroller.items[index].content = content; // Don't double-sanitize, it's done on render
1298
- // Re-render if the item is currently visible
1299
- this.virtualScroller.updateItem(index, {
1300
- content: content
1301
- });
1302
- }
1303
- } else {
1304
- // Regular DOM manipulation
1305
- this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))).lastChild.innerHTML = this._sanitizeContent(content);
1306
- }
707
+ msgEl.querySelector('.quikchat-message-content').innerHTML = this._processContent(content);
708
+ msgEl.classList.remove('quikchat-typing');
1307
709
  success = true;
1308
-
1309
- // Call the onMessageReplace callback if it exists
710
+ if (!this.userScrolledUp) {
711
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
712
+ }
1310
713
  if (this._onMessageReplace) {
1311
714
  this._onMessageReplace(this, n, content);
1312
715
  }
1313
-
1314
- // Don't auto-scroll on append - let user control this
1315
- // Users can call messageScrollToBottom() if they want to scroll
1316
- } catch (error) {
1317
- console.log("".concat(String(n), " : Message ID not found"));
716
+ } catch (_error) {
717
+ // Message ID not found
1318
718
  }
1319
719
  return success;
1320
720
  }
1321
-
1322
- /**
1323
- * Scrolls the messages area to the bottom.
1324
- */
1325
- }, {
1326
- key: "messageScrollToBottom",
1327
- value: function messageScrollToBottom() {
1328
- // Always use scrollTop to avoid page jumping
1329
- // This ensures only the chat container scrolls, not the entire page
1330
- this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
1331
- }
1332
-
1333
- /**
1334
- * Removes the last message from the messages area.
1335
- */
1336
- }, {
1337
- key: "messageRemoveLast",
1338
- value: function messageRemoveLast() {
1339
- // find the last message by id:
1340
- if (this._history.length >= 0) {
1341
- var lastMsgId = this._history[this._history.length - 1].msgid;
1342
- return this.messageRemove(lastMsgId);
1343
- }
1344
- return false;
1345
- }
1346
721
  }, {
1347
- key: "messageSetVisibility",
1348
- value: function messageSetVisibility(msgid, isVisible) {
1349
- var message = this._history.find(function (item) {
1350
- return item.msgid === msgid;
722
+ key: "messageAddTypingIndicator",
723
+ value: function messageAddTypingIndicator() {
724
+ var userString = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
725
+ var align = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'left';
726
+ var msgid = this.messageAddFull({
727
+ content: '',
728
+ userString: userString,
729
+ align: align,
730
+ role: 'assistant'
1351
731
  });
1352
- if (message && message.messageDiv) {
1353
- message.messageDiv.style.display = isVisible ? '' : 'none';
1354
- message.visible = isVisible;
1355
- this._updateMessageStyles();
1356
- return true;
1357
- }
1358
- return false;
732
+ var msgEl = this.messageGetDOMObject(msgid);
733
+ msgEl.classList.add('quikchat-typing');
734
+ var contentDiv = msgEl.querySelector('.quikchat-message-content');
735
+ contentDiv.innerHTML = '<span class="quikchat-typing-dots"><span>.</span><span>.</span><span>.</span></span>';
736
+ return msgid;
1359
737
  }
1360
738
  }, {
1361
- key: "messageGetVisibility",
1362
- value: function messageGetVisibility(msgid) {
1363
- var message = this._history.find(function (item) {
1364
- return item.msgid === msgid;
1365
- });
1366
- if (message && message.messageDiv) {
1367
- return message.messageDiv.style.display !== 'none';
1368
- }
1369
- return false; // Return false if not found or no messageDiv
739
+ key: "setMessageFormatter",
740
+ value: function setMessageFormatter(formatter) {
741
+ this._messageFormatter = formatter;
1370
742
  }
1371
743
  }, {
1372
- key: "_updateMessageStyles",
1373
- value: function _updateMessageStyles() {
1374
- var visibleMessages = _toConsumableArray(this._messagesArea.children).filter(function (child) {
1375
- return child.style.display !== 'none';
1376
- });
1377
- visibleMessages.forEach(function (messageDiv, index) {
1378
- messageDiv.classList.remove('quikchat-message-1', 'quikchat-message-2');
1379
- messageDiv.classList.add(index % 2 === 0 ? 'quikchat-message-1' : 'quikchat-message-2');
1380
- });
744
+ key: "setSanitize",
745
+ value: function setSanitize(sanitize) {
746
+ this._sanitize = sanitize;
1381
747
  }
1382
748
 
1383
- /**
1384
- * For right sided or centered messages, we need to handle the CSS for short and long messages.
1385
- * for short messages we use simple justifying, for long messages we need to wrap and perform multiline
1386
- * formatting.
1387
- *
1388
- * @param {*} messageElement
1389
- * @returns nothing
1390
- */
1391
- }, {
1392
- key: "_handleShortLongMessageCSS",
1393
- value: function _handleShortLongMessageCSS(messageElement, align) {
1394
- // console.log(messageElement);
1395
- // Reset classes
1396
- messageElement.classList.remove('left-singleline', 'left-multiline', 'center-singleline', 'center-multiline', 'right-singleline', 'right-multiline');
1397
- var contentDiv = messageElement.lastChild;
1398
- window.lastDiv = contentDiv; // for debugging
1399
- // Determine if the message is short or long
1400
-
1401
- var computedStyle = window.getComputedStyle(contentDiv);
1402
-
1403
- // Get the element's height
1404
- var elementHeight = contentDiv.offsetHeight;
1405
-
1406
- // Calculate or estimate line height
1407
- var lineHeight;
1408
- if (computedStyle.lineHeight === "normal") {
1409
- var fontSize = parseFloat(computedStyle.fontSize);
1410
- lineHeight = fontSize * 1.2; // approximate "normal" as 1.2 times font-size
1411
- } else {
1412
- lineHeight = parseFloat(computedStyle.lineHeight);
1413
- }
1414
-
1415
- // Check if the element height is more than one line-height
1416
- var isMultiLine = elementHeight > lineHeight;
1417
-
1418
- // Using scrollHeight and clientHeight to check for overflow (multi-line)
1419
- switch (align) {
1420
- case 'center':
1421
- if (isMultiLine) {
1422
- messageElement.classList.add('center-multiline');
1423
- } else {
1424
- messageElement.classList.add('center-singleline');
1425
- }
1426
- break;
1427
- case 'right':
1428
- if (isMultiLine) {
1429
- messageElement.classList.add('right-multiline');
1430
- } else {
1431
- messageElement.classList.add('right-singleline');
1432
- }
1433
- break;
1434
- case 'left':
1435
- default:
1436
- if (isMultiLine) {
1437
- messageElement.classList.add('left-multiline');
1438
- } else {
1439
- messageElement.classList.add('left-singleline');
1440
- }
1441
- break;
1442
- }
1443
- }
1444
749
  // history functions
1445
750
  /**
1446
- *
1447
- * @param {*} n
1448
- * @param {*} m
751
+ *
752
+ * @param {*} n
753
+ * @param {*} m
1449
754
  * @returns array of history messages
1450
755
  */
1451
- /**
1452
- * Gets a slice of message history
1453
- * @param {number} [n] - Start index (defaults to 0)
1454
- * @param {number} [m] - End index (defaults to history length)
1455
- * @returns {Array} Array of message objects
1456
- * @example
1457
- * // Get first 10 messages
1458
- * const messages = chat.historyGet(0, 10);
1459
- *
1460
- * // Get all messages
1461
- * const allMessages = chat.historyGet();
1462
- */
1463
756
  }, {
1464
757
  key: "historyGet",
1465
758
  value: function historyGet(n, m) {
1466
- if (n == undefined) {
1467
- n = 0;
1468
- m = this._history.length;
759
+ if (n === undefined) {
760
+ return this._history.slice();
1469
761
  }
1470
762
  if (m === undefined) {
1471
- m = n < 0 ? m : n + 1;
1472
- }
1473
- // remember that entries could be deleted. TODO: So we need to return the actual history entries
1474
- // so now we need to find the array index that correspondes to messageIds n (start) and m (end)
1475
-
1476
- return this._history.slice(n, m);
1477
- }
1478
-
1479
- /**
1480
- * Gets a copy of the entire message history
1481
- * @returns {Array} Complete array of all message objects
1482
- * @example
1483
- * const history = chat.historyGetAllCopy();
1484
- * console.log(`Total messages: ${history.length}`);
1485
- */
1486
- }, {
1487
- key: "historyGetAllCopy",
1488
- value: function historyGetAllCopy() {
1489
- return this._history.slice();
1490
- }
1491
-
1492
- /**
1493
- * Get a page of history messages with pagination support
1494
- * @param {number} page - Page number (1-based)
1495
- * @param {number} pageSize - Number of messages per page (default 50)
1496
- * @param {string} order - 'asc' for oldest first, 'desc' for newest first (default 'asc')
1497
- * @returns {object} Object with messages array, pagination info
1498
- */
1499
- }, {
1500
- key: "historyGetPage",
1501
- value: function historyGetPage() {
1502
- var page = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
1503
- var pageSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50;
1504
- var order = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'asc';
1505
- var totalMessages = this._history.length;
1506
- var totalPages = Math.ceil(totalMessages / pageSize);
1507
- var currentPage = Math.max(1, Math.min(page, totalPages || 1));
1508
- var start, end;
1509
- if (order === 'desc') {
1510
- // For descending order, page 1 shows the newest messages
1511
- start = Math.max(0, totalMessages - currentPage * pageSize);
1512
- end = totalMessages - (currentPage - 1) * pageSize;
1513
- } else {
1514
- // For ascending order, page 1 shows the oldest messages
1515
- start = (currentPage - 1) * pageSize;
1516
- end = Math.min(start + pageSize, totalMessages);
1517
- }
1518
- var messages = this._history.slice(start, end);
1519
-
1520
- // Reverse messages array if descending order requested
1521
- if (order === 'desc') {
1522
- messages.reverse();
1523
- }
1524
- return {
1525
- messages: messages,
1526
- pagination: {
1527
- currentPage: currentPage,
1528
- pageSize: pageSize,
1529
- totalPages: totalPages,
1530
- totalMessages: totalMessages,
1531
- hasNext: currentPage < totalPages,
1532
- hasPrevious: currentPage > 1,
1533
- order: order
763
+ if (n < 0) {
764
+ return this._history.slice(n);
1534
765
  }
1535
- };
1536
- }
1537
-
1538
- /**
1539
- * Get information about history size and pagination
1540
- * @param {number} pageSize - Size to calculate pages for (default 50)
1541
- * @returns {object} History metadata
1542
- */
1543
- /**
1544
- * Search history for messages matching criteria
1545
- * @param {object} criteria - Search criteria object
1546
- * @param {string} criteria.text - Text to search for in message content
1547
- * @param {string} criteria.userString - Filter by user name
1548
- * @param {string} criteria.role - Filter by role
1549
- * @param {array} criteria.tags - Filter by tags (messages with any of these tags)
1550
- * @param {number} criteria.limit - Maximum results to return (default 100)
1551
- * @returns {array} Array of matching messages
1552
- */
1553
- /**
1554
- * Searches through message history with various filters
1555
- * @param {Object} [criteria={}] - Search criteria
1556
- * @param {string} [criteria.text] - Text to search for in message content
1557
- * @param {string} [criteria.userString] - Filter by specific user
1558
- * @param {string} [criteria.role] - Filter by role (user, assistant, system)
1559
- * @param {string[]} [criteria.tags] - Filter by tags (messages must have at least one)
1560
- * @param {number} [criteria.limit=100] - Maximum number of results
1561
- * @returns {Array} Array of matching messages
1562
- * @since 1.1.15
1563
- * @example
1564
- * // Search for messages containing 'error'
1565
- * const errors = chat.historySearch({ text: 'error' });
1566
- *
1567
- * // Find all bot messages
1568
- * const botMessages = chat.historySearch({ role: 'assistant' });
1569
- *
1570
- * // Complex search
1571
- * const results = chat.historySearch({
1572
- * text: 'help',
1573
- * userString: 'Support',
1574
- * tags: ['urgent'],
1575
- * limit: 20
1576
- * });
1577
- */
1578
- }, {
1579
- key: "historySearch",
1580
- value: function historySearch() {
1581
- var criteria = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1582
- var results = this._history;
1583
-
1584
- // Filter by text content (case-insensitive)
1585
- if (criteria.text) {
1586
- var searchText = criteria.text.toLowerCase();
1587
- results = results.filter(function (msg) {
1588
- return msg.content.toLowerCase().includes(searchText);
1589
- });
766
+ return this._history.slice(n, n + 1);
1590
767
  }
1591
-
1592
- // Filter by user
1593
- if (criteria.userString) {
1594
- results = results.filter(function (msg) {
1595
- return msg.userString === criteria.userString;
1596
- });
1597
- }
1598
-
1599
- // Filter by role
1600
- if (criteria.role) {
1601
- results = results.filter(function (msg) {
1602
- return msg.role === criteria.role;
1603
- });
1604
- }
1605
-
1606
- // Filter by tags (match any tag)
1607
- if (criteria.tags && criteria.tags.length > 0) {
1608
- results = results.filter(function (msg) {
1609
- return msg.tags && msg.tags.some(function (tag) {
1610
- return criteria.tags.includes(tag);
1611
- });
1612
- });
1613
- }
1614
-
1615
- // Limit results
1616
- var limit = criteria.limit || 100;
1617
- if (results.length > limit) {
1618
- results = results.slice(0, limit);
1619
- }
1620
- return results;
1621
- }
1622
-
1623
- /**
1624
- * Gets metadata and statistics about the message history
1625
- * @param {number} [pageSize=50] - Page size for calculating total pages
1626
- * @returns {Object} History information object
1627
- * @returns {number} returns.totalMessages - Total number of messages
1628
- * @returns {number} returns.totalPages - Total pages based on page size
1629
- * @returns {Object|null} returns.oldestMessage - First message info
1630
- * @returns {Object|null} returns.newestMessage - Last message info
1631
- * @returns {Object} returns.memoryUsage - Memory usage statistics
1632
- * @since 1.1.15
1633
- * @example
1634
- * const info = chat.historyGetInfo();
1635
- * console.log(`Messages: ${info.totalMessages}`);
1636
- * console.log(`Memory: ${info.memoryUsage.estimatedSize} bytes`);
1637
- */
1638
- }, {
1639
- key: "historyGetInfo",
1640
- value: function historyGetInfo() {
1641
- var pageSize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 50;
1642
- var totalMessages = this._history.length;
1643
- return {
1644
- totalMessages: totalMessages,
1645
- totalPages: Math.ceil(totalMessages / pageSize),
1646
- oldestMessage: totalMessages > 0 ? {
1647
- msgid: this._history[0].msgid,
1648
- timestamp: this._history[0].timestamp,
1649
- userString: this._history[0].userString
1650
- } : null,
1651
- newestMessage: totalMessages > 0 ? {
1652
- msgid: this._history[totalMessages - 1].msgid,
1653
- timestamp: this._history[totalMessages - 1].timestamp,
1654
- userString: this._history[totalMessages - 1].userString
1655
- } : null,
1656
- memoryUsage: {
1657
- estimatedSize: JSON.stringify(this._history).length,
1658
- averageMessageSize: totalMessages > 0 ? Math.round(JSON.stringify(this._history).length / totalMessages) : 0
1659
- }
1660
- };
768
+ return this._history.slice(n, m);
1661
769
  }
1662
-
1663
- /**
1664
- * Clears all messages and resets the chat
1665
- * @returns {void}
1666
- * @example
1667
- * chat.historyClear(); // Removes all messages
1668
- */
1669
770
  }, {
1670
771
  key: "historyClear",
1671
772
  value: function historyClear() {
1672
773
  this.msgid = 0;
1673
-
1674
- // Handle virtual scroller
1675
- if (this.virtualScroller) {
1676
- this.virtualScroller.clear();
1677
- } else {
1678
- this._messagesArea.innerHTML = "";
1679
- }
1680
774
  this._history = [];
1681
- this._activeTags.clear();
1682
775
  }
1683
776
  }, {
1684
777
  key: "historyGetLength",
@@ -1696,29 +789,57 @@ var quikchat = /*#__PURE__*/function () {
1696
789
  }, {
1697
790
  key: "historyGetMessageContent",
1698
791
  value: function historyGetMessageContent(n) {
1699
- if (n >= 0 && n < this._history.length) return this._history[n].content;else return "";
792
+ if (n >= 0 && n < this._history.length) {
793
+ return this._history[n].content;
794
+ }
795
+ return "";
1700
796
  }
1701
-
1702
- // expects an array of messages to be in the format of the history object
1703
797
  }, {
1704
- key: "historyRestoreAll",
1705
- value: function historyRestoreAll(messageList) {
1706
- var _this9 = this;
1707
- // clear all messages and history
1708
- this.historyClear();
1709
-
1710
- // clear the messages div
1711
- this._messagesArea.innerHTML = "";
1712
-
1713
- // add all messages
1714
- messageList.forEach(function (message) {
1715
- _this9.messageAddFull(message);
798
+ key: "historyExport",
799
+ value: function historyExport() {
800
+ return this._history.map(function (item) {
801
+ return {
802
+ msgid: item.msgid,
803
+ content: item.content,
804
+ userString: item.userString,
805
+ align: item.align,
806
+ role: item.role,
807
+ userID: item.userID,
808
+ visible: item.visible,
809
+ tags: item.tags ? item.tags.slice() : [],
810
+ timestamp: item.timestamp,
811
+ updatedtime: item.updatedtime
812
+ };
1716
813
  });
1717
814
  }
1718
- /**
1719
- *
1720
- * @param {string} newTheme
1721
- */
815
+ }, {
816
+ key: "historyImport",
817
+ value: function historyImport(data) {
818
+ // Clear existing messages from DOM and history
819
+ this._messagesArea.innerHTML = '';
820
+ this._history = [];
821
+ this.msgid = 0;
822
+ var _iterator2 = _createForOfIteratorHelper(data),
823
+ _step2;
824
+ try {
825
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
826
+ var entry = _step2.value;
827
+ this.messageAddFull({
828
+ content: entry.content || '',
829
+ userString: entry.userString || 'user',
830
+ align: entry.align || 'right',
831
+ role: entry.role || 'user',
832
+ userID: entry.userID,
833
+ visible: entry.visible,
834
+ tags: entry.tags
835
+ });
836
+ }
837
+ } catch (err) {
838
+ _iterator2.e(err);
839
+ } finally {
840
+ _iterator2.f();
841
+ }
842
+ }
1722
843
  }, {
1723
844
  key: "changeTheme",
1724
845
  value: function changeTheme(newTheme) {
@@ -1726,323 +847,46 @@ var quikchat = /*#__PURE__*/function () {
1726
847
  this._chatWidget.classList.add(newTheme);
1727
848
  this._theme = newTheme;
1728
849
  }
1729
-
1730
- /**
1731
- * Get the current theme
1732
- * @returns {string} - The current theme
1733
- */
1734
850
  }, {
1735
851
  key: "theme",
1736
852
  get: function get() {
1737
853
  return this._theme;
1738
854
  }
1739
-
1740
- /**
1741
- *
1742
- * @returns {object} - Returns the version and license information for the library.
1743
- */
1744
- /**
1745
- * Gets the QuikChat version information
1746
- * @static
1747
- * @returns {Object} Version information object
1748
- * @returns {string} returns.version - Version number (e.g., '1.1.15')
1749
- * @returns {string} returns.license - License type (e.g., 'BSD-2')
1750
- * @returns {string} returns.url - Project URL
1751
- * @returns {boolean} returns.fun - Easter egg flag
1752
- * @example
1753
- * const version = quikchat.version();
1754
- * console.log(`QuikChat v${version.version}`);
1755
- */
1756
- }, {
1757
- key: "setTagVisibility",
1758
- value:
1759
- /**
1760
- * Sets visibility for all messages with a specific tag
1761
- * @param {string} tagName - Tag name to control
1762
- * @param {boolean} isVisible - Whether to show or hide messages with this tag
1763
- * @returns {boolean} True if successful, false if invalid tag name
1764
- * @since 1.1.14
1765
- * @example
1766
- * // Hide all system messages
1767
- * chat.setTagVisibility('system', false);
1768
- *
1769
- * // Show urgent messages
1770
- * chat.setTagVisibility('urgent', true);
1771
- */
1772
- function setTagVisibility(tagName, isVisible) {
1773
- if (typeof tagName !== 'string' || !/^[a-zA-Z0-9-]+$/.test(tagName)) {
1774
- return false;
1775
- }
1776
- var className = "quikchat-show-tag-".concat(tagName);
1777
- if (isVisible) {
1778
- this._chatWidget.classList.add(className);
1779
- } else {
1780
- this._chatWidget.classList.remove(className);
1781
- }
1782
- this._updateMessageStyles();
1783
- return true;
1784
- }
1785
-
1786
- /**
1787
- * Gets the visibility state of a tag
1788
- * @param {string} tagName - Tag name to check
1789
- * @returns {boolean} True if tag is visible, false otherwise
1790
- * @since 1.1.14
1791
- * @example
1792
- * const isVisible = chat.getTagVisibility('system');
1793
- */
1794
- }, {
1795
- key: "getTagVisibility",
1796
- value: function getTagVisibility(tagName) {
1797
- if (typeof tagName !== 'string' || !/^[a-zA-Z0-9-]+$/.test(tagName)) {
1798
- return false;
1799
- }
1800
- return this._chatWidget.classList.contains("quikchat-show-tag-".concat(tagName));
1801
- }
1802
-
1803
- /**
1804
- * Gets all active tags in the chat
1805
- * @returns {string[]} Array of all tags currently in use
1806
- * @since 1.1.14
1807
- * @example
1808
- * const tags = chat.getActiveTags();
1809
- * console.log('Active tags:', tags);
1810
- */
1811
- }, {
1812
- key: "getActiveTags",
1813
- value: function getActiveTags() {
1814
- return Array.from(this._activeTags);
1815
- }
1816
-
1817
- /**
1818
- * Checks if virtual scrolling is currently enabled
1819
- * @returns {boolean} True if virtual scrolling is enabled, false otherwise
1820
- * @since 1.1.16
1821
- * @example
1822
- * if (chat.isVirtualScrollingEnabled()) {
1823
- * console.log('Virtual scrolling is active');
1824
- * }
1825
- */
1826
- }, {
1827
- key: "isVirtualScrollingEnabled",
1828
- value: function isVirtualScrollingEnabled() {
1829
- return this.virtualScrollingEnabled && this.virtualScroller !== null;
1830
- }
1831
-
1832
- /**
1833
- * Gets the virtual scrolling configuration
1834
- * @returns {Object} Virtual scrolling configuration with enabled status and threshold
1835
- * @since 1.1.16
1836
- * @example
1837
- * const config = chat.getVirtualScrollingConfig();
1838
- * console.log(`Virtual scrolling: ${config.enabled}, threshold: ${config.threshold}`);
1839
- */
1840
- }, {
1841
- key: "getVirtualScrollingConfig",
1842
- value: function getVirtualScrollingConfig() {
1843
- return {
1844
- enabled: this.virtualScrollingEnabled,
1845
- active: this.virtualScroller !== null,
1846
- threshold: this.virtualScrollingThreshold
1847
- };
1848
- }
1849
-
1850
- /**
1851
- * Set the language for the widget
1852
- * @param {string} lang - Language code (e.g., 'en', 'es', 'fr')
1853
- * @param {Object} [translations] - Optional translations object for the language
1854
- * @example
1855
- * chat.setLanguage('es', {
1856
- * sendButton: 'Enviar',
1857
- * inputPlaceholder: 'Escribe un mensaje...',
1858
- * titleDefault: 'Chat'
1859
- * });
1860
- */
1861
- }, {
1862
- key: "setLanguage",
1863
- value: function setLanguage(lang, translations) {
1864
- this.lang = lang;
1865
-
1866
- // Add translations if provided
1867
- if (translations) {
1868
- this.translations[lang] = _objectSpread2(_objectSpread2({}, this.translations[lang]), translations);
1869
- }
1870
-
1871
- // Update current translations
1872
- this.currentTranslations = this.translations[lang] || this.translations['en'];
1873
-
1874
- // Update UI elements
1875
- this._updateUITranslations();
1876
- }
1877
-
1878
- /**
1879
- * Get current language
1880
- * @returns {string} Current language code
1881
- */
1882
- }, {
1883
- key: "getLanguage",
1884
- value: function getLanguage() {
1885
- return this.lang;
1886
- }
1887
-
1888
- /**
1889
- * Set text direction (LTR or RTL)
1890
- * @param {string} dir - Direction ('ltr' or 'rtl')
1891
- */
1892
- }, {
1893
- key: "setDirection",
1894
- value: function setDirection(dir) {
1895
- if (dir === 'ltr' || dir === 'rtl') {
1896
- this.dir = dir;
1897
- this._chatWidget.setAttribute('dir', dir);
1898
- }
1899
- }
1900
-
1901
- /**
1902
- * Get current text direction
1903
- * @returns {string} Current direction ('ltr' or 'rtl')
1904
- */
1905
- }, {
1906
- key: "getDirection",
1907
- value: function getDirection() {
1908
- return this.dir;
1909
- }
1910
-
1911
- /**
1912
- * Update UI elements with current translations
1913
- * @private
1914
- */
1915
- }, {
1916
- key: "_updateUITranslations",
1917
- value: function _updateUITranslations() {
1918
- // Update send button text
1919
- if (this._sendButton) {
1920
- this._sendButton.textContent = this.currentTranslations.sendButton || 'Send';
1921
- }
1922
-
1923
- // Update input placeholder
1924
- if (this._textEntry) {
1925
- this._textEntry.placeholder = this.currentTranslations.inputPlaceholder || 'Type a message...';
1926
- }
1927
-
1928
- // Update widget language attribute
1929
- if (this._chatWidget) {
1930
- this._chatWidget.setAttribute('lang', this.lang);
1931
- }
1932
- }
1933
-
1934
- /**
1935
- * Sets the content sanitizer function
1936
- * @param {Function|null} sanitizer - Function to sanitize content or null to disable
1937
- * @returns {void}
1938
- * @example
1939
- * // Use built-in HTML escaper
1940
- * chat.setSanitizer(quikchat.sanitizers.escapeHTML);
1941
- *
1942
- * // Use custom sanitizer (e.g., DOMPurify)
1943
- * chat.setSanitizer((content) => DOMPurify.sanitize(content));
1944
- *
1945
- * // Disable sanitization
1946
- * chat.setSanitizer(null);
1947
- */
1948
- }, {
1949
- key: "setSanitizer",
1950
- value: function setSanitizer(sanitizer) {
1951
- if (sanitizer === null || typeof sanitizer === 'function') {
1952
- this._sanitizer = sanitizer;
1953
- } else {
1954
- console.warn('Sanitizer must be a function or null');
1955
- }
1956
- }
1957
-
1958
- /**
1959
- * Gets the current content sanitizer function
1960
- * @returns {Function|null} The current sanitizer function or null
1961
- * @example
1962
- * const sanitizer = chat.getSanitizer();
1963
- * if (sanitizer) {
1964
- * console.log('Sanitization is enabled');
1965
- * }
1966
- */
1967
- }, {
1968
- key: "getSanitizer",
1969
- value: function getSanitizer() {
1970
- return this._sanitizer;
1971
- }
1972
-
1973
- /**
1974
- * Internal method to apply sanitizer to content
1975
- * @private
1976
- * @param {string} content - Content to sanitize
1977
- * @returns {string} Sanitized content
1978
- */
1979
- }, {
1980
- key: "_sanitizeContent",
1981
- value: function _sanitizeContent(content) {
1982
- if (this._sanitizer && typeof this._sanitizer === 'function') {
1983
- return this._sanitizer(content);
1984
- }
1985
- return content;
1986
- }
1987
855
  }], [{
1988
856
  key: "version",
1989
857
  value: function version() {
1990
- return quikchatVersion;
858
+ return {
859
+ "version": "1.2.4",
860
+ "license": "BSD-2",
861
+ "url": "https://github/deftio/quikchat"
862
+ };
1991
863
  }
1992
864
 
1993
- /**
1994
- * Built-in content sanitizers for XSS protection
1995
- * @static
1996
- * @type {Object}
1997
- * @property {Function} escapeHTML - Escapes HTML entities
1998
- * @property {Function} stripHTML - Removes all HTML tags
1999
- * @example
2000
- * // Use built-in HTML escaper
2001
- * const chat = new quikchat('#chat', onSend, {
2002
- * sanitizer: quikchat.sanitizers.escapeHTML
2003
- * });
2004
- */
2005
- }, {
2006
- key: "loremIpsum",
2007
- value:
2008
865
  /**
2009
866
  * quikchat.loremIpsum() - Generate a simple string of Lorem Ipsum text (sample typographer's text) of numChars in length.
2010
867
  * borrowed from github.com/deftio/bitwrench.js
2011
- * @param {number} numChars - The number of characters to generate (random btw 25 and 150 if undefined).
868
+ * @param {number} numChars - The number of characters to generate (random btw 25 and 150 if undefined).
2012
869
  * @param {number} [startSpot=0] - The starting index in the Lorem Ipsum text. If undefined, a random startSpot will be generated.
2013
870
  * @param {boolean} [startWithCapitalLetter=true] - If true, capitalize the first character or inject a capital letter if the first character isn't a capital letter.
2014
- *
871
+ *
2015
872
  * @returns {string} A string of Lorem Ipsum text.
2016
- *
2017
- * @example
873
+ *
874
+ * @example
2018
875
  * // Returns 200 characters of Lorem Ipsum starting from index 50
2019
876
  * loremIpsum(200, 50);
2020
- *
2021
- * @example
877
+ *
878
+ * @example
2022
879
  * //Returns a 200 Lorem Ipsum characters starting from a random index
2023
880
  * loremIpsum(200);
2024
881
  */
2025
-
2026
- /**
2027
- * Generates Lorem Ipsum placeholder text
2028
- * @static
2029
- * @param {number} [numChars] - Length of text to generate (random if not specified)
2030
- * @param {number} [startSpot] - Starting offset in Lorem text (random if not specified)
2031
- * @param {boolean} [startWithCapitalLetter=true] - Whether to capitalize first letter
2032
- * @returns {string} Generated Lorem Ipsum text
2033
- * @example
2034
- * // Generate 100 characters
2035
- * const text = quikchat.loremIpsum(100);
2036
- *
2037
- * // Generate random length
2038
- * const randomText = quikchat.loremIpsum();
2039
- */
2040
- function loremIpsum(numChars) {
882
+ }, {
883
+ key: "loremIpsum",
884
+ value: function loremIpsum(numChars) {
2041
885
  var startSpot = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
2042
886
  var startWithCapitalLetter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
2043
887
  var loremText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
2044
888
  if (typeof numChars !== "number") {
2045
- numChars = Math.floor(Math.random() * 150) + 25;
889
+ numChars = Math.floor(Math.random() * 126) + 25;
2046
890
  }
2047
891
  if (startSpot === undefined) {
2048
892
  startSpot = Math.floor(Math.random() * loremText.length);
@@ -2054,9 +898,6 @@ var quikchat = /*#__PURE__*/function () {
2054
898
  startSpot = (startSpot + 1) % loremText.length;
2055
899
  }
2056
900
  var l = loremText.substring(startSpot) + loremText.substring(0, startSpot);
2057
- if (typeof numChars !== "number") {
2058
- numChars = l.length;
2059
- }
2060
901
  var s = "";
2061
902
  while (numChars > 0) {
2062
903
  s += numChars < l.length ? l.substring(0, numChars) : l;
@@ -2066,128 +907,12 @@ var quikchat = /*#__PURE__*/function () {
2066
907
  s = s.substring(0, s.length - 1) + "."; // always end on non-whitespace. "." was chosen arbitrarily.
2067
908
  }
2068
909
  if (startWithCapitalLetter) {
2069
- var c = s[0].toUpperCase();
2070
- c = /[A-Z]/.test(c) ? c : "M";
2071
- s = c + s.substring(1);
910
+ s = s[0].toUpperCase() + s.substring(1);
2072
911
  }
2073
912
  return s;
2074
913
  }
2075
- }, {
2076
- key: "tempMessageGenerator",
2077
- value:
2078
- /**
2079
- * Creates a temporary message that updates periodically
2080
- * @static
2081
- * @param {string|HTMLElement} domElement - Element selector or DOM element
2082
- * @param {string} content - Initial message content
2083
- * @param {number} interval - Update interval in milliseconds (min 100ms)
2084
- * @param {Function} [cb=null] - Callback to generate new content
2085
- * @param {string} cb.message - Current message
2086
- * @param {number} cb.count - Update count
2087
- * @returns {void}
2088
- * @example
2089
- * // Simple loading indicator
2090
- * quikchat.tempMessageGenerator('#loading', 'Loading', 500);
2091
- *
2092
- * // Custom update function
2093
- * quikchat.tempMessageGenerator('#status', 'Processing', 1000, (msg, count) => {
2094
- * return `Processing... ${count}%`;
2095
- * });
2096
- */
2097
- function tempMessageGenerator(domElement, content, interval) {
2098
- var cb = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
2099
- interval = Math.max(interval, 100); // Ensure at least 100ms interval
2100
-
2101
- var count = 0;
2102
- var defaultCB = function defaultCB(msg, count) {
2103
- msg += ".";
2104
- return msg;
2105
- };
2106
- if (cb && typeof cb !== 'function') {
2107
- cb = null;
2108
- }
2109
- cb = cb || defaultCB;
2110
-
2111
- // if its a string, then get the element (css sel) or its an DOM element already
2112
- var el = domElement;
2113
- if (typeof el === 'string') {
2114
- el = document.querySelector(el);
2115
- }
2116
- var element = el;
2117
-
2118
- // Ensure the element exists
2119
- if (!element) return;
2120
- element.innerHTML = content;
2121
- var currentMsg = content;
2122
- var intervalId = setInterval(function () {
2123
- if (element.innerHTML !== currentMsg) {
2124
- clearInterval(intervalId); // Stop updating if content is changed externally
2125
- return;
2126
- }
2127
- currentMsg = String(cb(currentMsg, count)); // Use callback return value if provided
2128
-
2129
- count++;
2130
- element.innerHTML = currentMsg;
2131
- }, interval);
2132
- }
2133
- }, {
2134
- key: "createTempMessageDOMStr",
2135
- value: function createTempMessageDOMStr(initialContent) {
2136
- var updateInterval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
2137
- var callback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
2138
- var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
2139
- // Make sure the interval is at least 100ms
2140
- updateInterval = Math.max(updateInterval, 100);
2141
-
2142
- // Validate callback; if not a function, ignore it.
2143
- if (callback && typeof callback !== 'function') {
2144
- callback = null;
2145
- }
2146
- // Default callback simply appends a dot.
2147
- callback = callback || function (msg, count) {
2148
- return msg + ".";
2149
- };
2150
-
2151
- // Allow an optional CSS class for the container element
2152
- var containerClass = options.containerClass ? options.containerClass : '';
2153
-
2154
- // Generate a unique id so that the inline script can reliably find the container.
2155
- var uniqueId = "tempMsg_" + Date.now() + "_" + Math.floor(Math.random() * 1000000);
2156
-
2157
- // Build and return the HTML string.
2158
- // Note the use of <\/script> (with a backslash) so that the inline script is not terminated early.
2159
- return "\n <span id=\"".concat(uniqueId, "\" ").concat(containerClass ? "class=\"".concat(containerClass, "\"") : '', ">\n ").concat(initialContent, "\n </span>\n <script>\n (function(){\n // Get our container element by its unique id.\n var container = document.getElementById(\"").concat(uniqueId, "\");\n if (!container) return;\n var count = 0;\n var currentMsg = container.innerHTML;\n var interval = ").concat(updateInterval, ";\n // Convert the callback function into its string representation.\n var cb = ").concat(callback.toString(), ";\n var intervalId = setInterval(function(){\n // If the content has been replaced, stop updating.\n if(container.innerHTML !== currentMsg){\n clearInterval(intervalId);\n return;\n }\n // Use the callback to generate the new message.\n currentMsg = String(cb(currentMsg, count));\n count++;\n container.innerHTML = currentMsg;\n }, interval);\n })();\n </script>\n ");
2160
- }
2161
914
  }]);
2162
915
  }();
2163
- _defineProperty(quikchat, "sanitizers", {
2164
- /**
2165
- * Escapes HTML entities to prevent XSS
2166
- * @param {string} str - String to escape
2167
- * @returns {string} Escaped string
2168
- */
2169
- escapeHTML: function escapeHTML(str) {
2170
- if (typeof str !== 'string') return str;
2171
- return str.replace(/[&<>"']/g, function (m) {
2172
- return {
2173
- '&': '&amp;',
2174
- '<': '&lt;',
2175
- '>': '&gt;',
2176
- '"': '&quot;',
2177
- "'": '&#39;'
2178
- }[m];
2179
- });
2180
- },
2181
- /**
2182
- * Strips all HTML tags but keeps text content
2183
- * @param {string} str - String to strip
2184
- * @returns {string} Text without HTML tags
2185
- */
2186
- stripHTML: function stripHTML(str) {
2187
- if (typeof str !== 'string') return str;
2188
- return str.replace(/<[^>]*>/g, '');
2189
- }
2190
- });
2191
916
 
2192
917
  export { quikchat as default };
2193
918
  //# sourceMappingURL=quikchat.esm.js.map