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