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
@@ -0,0 +1,1647 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.quikchat = factory());
5
+ })(this, (function () { 'use strict';
6
+
7
+ function _arrayLikeToArray(r, a) {
8
+ (null == a || a > r.length) && (a = r.length);
9
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10
+ return n;
11
+ }
12
+ function _assertThisInitialized(e) {
13
+ if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
14
+ return e;
15
+ }
16
+ function _callSuper(t, o, e) {
17
+ return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
18
+ }
19
+ function _classCallCheck(a, n) {
20
+ if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
21
+ }
22
+ function _defineProperties(e, r) {
23
+ for (var t = 0; t < r.length; t++) {
24
+ var o = r[t];
25
+ o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o);
26
+ }
27
+ }
28
+ function _createClass(e, r, t) {
29
+ return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
30
+ writable: false
31
+ }), e;
32
+ }
33
+ function _createForOfIteratorHelper(r, e) {
34
+ var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
35
+ if (!t) {
36
+ if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
37
+ t && (r = t);
38
+ var n = 0,
39
+ F = function () {};
40
+ return {
41
+ s: F,
42
+ n: function () {
43
+ return n >= r.length ? {
44
+ done: true
45
+ } : {
46
+ done: false,
47
+ value: r[n++]
48
+ };
49
+ },
50
+ e: function (r) {
51
+ throw r;
52
+ },
53
+ f: F
54
+ };
55
+ }
56
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
57
+ }
58
+ var o,
59
+ a = true,
60
+ u = false;
61
+ return {
62
+ s: function () {
63
+ t = t.call(r);
64
+ },
65
+ n: function () {
66
+ var r = t.next();
67
+ return a = r.done, r;
68
+ },
69
+ e: function (r) {
70
+ u = true, o = r;
71
+ },
72
+ f: function () {
73
+ try {
74
+ a || null == t.return || t.return();
75
+ } finally {
76
+ if (u) throw o;
77
+ }
78
+ }
79
+ };
80
+ }
81
+ function _defineProperty(e, r, t) {
82
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
83
+ value: t,
84
+ enumerable: true,
85
+ configurable: true,
86
+ writable: true
87
+ }) : e[r] = t, e;
88
+ }
89
+ function _getPrototypeOf(t) {
90
+ return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
91
+ return t.__proto__ || Object.getPrototypeOf(t);
92
+ }, _getPrototypeOf(t);
93
+ }
94
+ function _inherits(t, e) {
95
+ if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
96
+ t.prototype = Object.create(e && e.prototype, {
97
+ constructor: {
98
+ value: t,
99
+ writable: true,
100
+ configurable: true
101
+ }
102
+ }), Object.defineProperty(t, "prototype", {
103
+ writable: false
104
+ }), e && _setPrototypeOf(t, e);
105
+ }
106
+ function _isNativeReflectConstruct() {
107
+ try {
108
+ var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
109
+ } catch (t) {}
110
+ return (_isNativeReflectConstruct = function () {
111
+ return !!t;
112
+ })();
113
+ }
114
+ function ownKeys(e, r) {
115
+ var t = Object.keys(e);
116
+ if (Object.getOwnPropertySymbols) {
117
+ var o = Object.getOwnPropertySymbols(e);
118
+ r && (o = o.filter(function (r) {
119
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
120
+ })), t.push.apply(t, o);
121
+ }
122
+ return t;
123
+ }
124
+ function _objectSpread2(e) {
125
+ for (var r = 1; r < arguments.length; r++) {
126
+ var t = null != arguments[r] ? arguments[r] : {};
127
+ r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
128
+ _defineProperty(e, r, t[r]);
129
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
130
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
131
+ });
132
+ }
133
+ return e;
134
+ }
135
+ function _possibleConstructorReturn(t, e) {
136
+ if (e && ("object" == typeof e || "function" == typeof e)) return e;
137
+ if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
138
+ return _assertThisInitialized(t);
139
+ }
140
+ function _setPrototypeOf(t, e) {
141
+ return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
142
+ return t.__proto__ = e, t;
143
+ }, _setPrototypeOf(t, e);
144
+ }
145
+ function _toPrimitive(t, r) {
146
+ if ("object" != typeof t || !t) return t;
147
+ var e = t[Symbol.toPrimitive];
148
+ if (void 0 !== e) {
149
+ var i = e.call(t, r);
150
+ if ("object" != typeof i) return i;
151
+ throw new TypeError("@@toPrimitive must return a primitive value.");
152
+ }
153
+ return ("string" === r ? String : Number)(t);
154
+ }
155
+ function _toPropertyKey(t) {
156
+ var i = _toPrimitive(t, "string");
157
+ return "symbol" == typeof i ? i : i + "";
158
+ }
159
+ function _unsupportedIterableToArray(r, a) {
160
+ if (r) {
161
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
162
+ var t = {}.toString.call(r).slice(8, -1);
163
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
164
+ }
165
+ }
166
+
167
+ var quikchat = /*#__PURE__*/function () {
168
+ /**
169
+ *
170
+ * @param string or DOM element parentElement
171
+ * @param {*} meta
172
+ */
173
+ function quikchat(parentElement) {
174
+ var onSend = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
175
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
176
+ _classCallCheck(this, quikchat);
177
+ var defaultOpts = {
178
+ theme: 'quikchat-theme-light',
179
+ trackHistory: true,
180
+ showTimestamps: false,
181
+ titleArea: {
182
+ title: "Chat",
183
+ show: false,
184
+ align: "center"
185
+ },
186
+ messagesArea: {
187
+ alternating: true
188
+ }
189
+ };
190
+ var meta = _objectSpread2(_objectSpread2({}, defaultOpts), options); // merge options with defaults
191
+
192
+ if (typeof parentElement === 'string') {
193
+ parentElement = document.querySelector(parentElement);
194
+ }
195
+ this._parentElement = parentElement;
196
+ this._theme = meta.theme;
197
+ this._onSend = onSend ? onSend : function () {}; // call back function for onSend
198
+ this._messageFormatter = meta.messageFormatter || null;
199
+ this._sanitize = meta.sanitize || false;
200
+ this._createWidget();
201
+ // title area
202
+ if (meta.titleArea) {
203
+ this.titleAreaSetContents(meta.titleArea.title, meta.titleArea.align);
204
+ if (meta.titleArea.show === true) {
205
+ this.titleAreaShow();
206
+ } else {
207
+ this.titleAreaHide();
208
+ }
209
+ }
210
+ // messages area
211
+ if (meta.messagesArea) {
212
+ this.messagesAreaAlternateColors(meta.messagesArea.alternating);
213
+ }
214
+ // timestamps
215
+ if (meta.showTimestamps) {
216
+ this.messagesAreaShowTimestamps(true);
217
+ }
218
+ // direction (ltr/rtl)
219
+ if (meta.direction) {
220
+ this.setDirection(meta.direction);
221
+ }
222
+ // plumbing
223
+ this._attachEventListeners();
224
+ this.trackHistory = meta.trackHistory !== false;
225
+ this._historyLimit = 10000000;
226
+ this._history = [];
227
+ }
228
+ return _createClass(quikchat, [{
229
+ key: "_createWidget",
230
+ value: function _createWidget() {
231
+ 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 ");
232
+ this._parentElement.innerHTML = widgetHTML;
233
+ this._chatWidget = this._parentElement.querySelector('.quikchat-base');
234
+ this._titleArea = this._chatWidget.querySelector('.quikchat-title-area');
235
+ this._messagesWrapper = this._chatWidget.querySelector('.quikchat-messages-wrapper');
236
+ this._messagesArea = this._chatWidget.querySelector('.quikchat-messages-area');
237
+ this._scrollBottomBtn = this._messagesWrapper.querySelector('.quikchat-scroll-bottom');
238
+ this._inputArea = this._chatWidget.querySelector('.quikchat-input-area');
239
+ this._textEntry = this._inputArea.querySelector('.quikchat-input-textbox');
240
+ this._sendButton = this._inputArea.querySelector('.quikchat-input-send-btn');
241
+ this.msgid = 0;
242
+ }
243
+
244
+ /**
245
+ * Attach event listeners to the widget
246
+ */
247
+ }, {
248
+ key: "_attachEventListeners",
249
+ value: function _attachEventListeners() {
250
+ var _this = this;
251
+ this._sendButton.addEventListener('click', function () {
252
+ var text = _this._textEntry.value.trim();
253
+ if (text === '') return;
254
+ _this._onSend(_this, text);
255
+ });
256
+ this._textEntry.addEventListener('keydown', function (event) {
257
+ // Check if Shift + Enter is pressed
258
+ if (event.shiftKey && event.keyCode === 13) {
259
+ event.preventDefault();
260
+ var text = _this._textEntry.value.trim();
261
+ if (text === '') return;
262
+ _this._onSend(_this, text);
263
+ }
264
+ });
265
+
266
+ // Auto-grow textarea
267
+ this._textEntry.addEventListener('input', function () {
268
+ return _this._autoGrowTextarea();
269
+ });
270
+ this._messagesArea.addEventListener('scroll', function () {
271
+ var _this$_messagesArea = _this._messagesArea,
272
+ scrollTop = _this$_messagesArea.scrollTop,
273
+ scrollHeight = _this$_messagesArea.scrollHeight,
274
+ clientHeight = _this$_messagesArea.clientHeight;
275
+ _this.userScrolledUp = scrollTop + clientHeight < scrollHeight - 1;
276
+ _this._updateScrollBottomBtn();
277
+ });
278
+
279
+ // Scroll-to-bottom button
280
+ this._scrollBottomBtn.addEventListener('click', function () {
281
+ return _this.scrollToBottom();
282
+ });
283
+
284
+ // Ctrl+End to scroll to bottom
285
+ this._chatWidget.addEventListener('keydown', function (event) {
286
+ if (event.ctrlKey && event.key === 'End') {
287
+ event.preventDefault();
288
+ _this.scrollToBottom();
289
+ }
290
+ });
291
+
292
+ // Use ResizeObserver to detect parent container resize
293
+ if (typeof ResizeObserver !== 'undefined') {
294
+ this._resizeObserver = new ResizeObserver(function () {
295
+ return _this._handleContainerResize();
296
+ });
297
+ this._resizeObserver.observe(this._parentElement);
298
+ }
299
+ }
300
+
301
+ // set the onSend function callback.
302
+ }, {
303
+ key: "setCallbackOnSend",
304
+ value: function setCallbackOnSend(callback) {
305
+ this._onSend = callback;
306
+ }
307
+ // set a callback for everytime a message is added (listener)
308
+ }, {
309
+ key: "setCallbackonMessageAdded",
310
+ value: function setCallbackonMessageAdded(callback) {
311
+ this._onMessageAdded = callback;
312
+ }
313
+ }, {
314
+ key: "setCallbackonMessageAppend",
315
+ value: function setCallbackonMessageAppend(callback) {
316
+ this._onMessageAppend = callback;
317
+ }
318
+ }, {
319
+ key: "setCallbackonMessageReplace",
320
+ value: function setCallbackonMessageReplace(callback) {
321
+ this._onMessageReplace = callback;
322
+ }
323
+ }, {
324
+ key: "setCallbackonMessageDelete",
325
+ value: function setCallbackonMessageDelete(callback) {
326
+ this._onMessageDelete = callback;
327
+ }
328
+
329
+ // Public methods
330
+ }, {
331
+ key: "titleAreaToggle",
332
+ value: function titleAreaToggle() {
333
+ if (this._titleArea.style.display === 'none') {
334
+ this.titleAreaShow();
335
+ } else {
336
+ this.titleAreaHide();
337
+ }
338
+ }
339
+ }, {
340
+ key: "titleAreaShow",
341
+ value: function titleAreaShow() {
342
+ this._titleArea.style.display = '';
343
+ }
344
+ }, {
345
+ key: "titleAreaHide",
346
+ value: function titleAreaHide() {
347
+ this._titleArea.style.display = 'none';
348
+ }
349
+ }, {
350
+ key: "titleAreaSetContents",
351
+ value: function titleAreaSetContents(title) {
352
+ var align = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center';
353
+ this._titleArea.innerHTML = title;
354
+ this._titleArea.style.textAlign = align;
355
+ }
356
+ }, {
357
+ key: "titleAreaGetContents",
358
+ value: function titleAreaGetContents() {
359
+ return this._titleArea.innerHTML;
360
+ }
361
+ }, {
362
+ key: "inputAreaToggle",
363
+ value: function inputAreaToggle() {
364
+ if (this._inputArea.style.display === 'none') {
365
+ this.inputAreaShow();
366
+ } else {
367
+ this.inputAreaHide();
368
+ }
369
+ }
370
+ }, {
371
+ key: "inputAreaShow",
372
+ value: function inputAreaShow() {
373
+ this._inputArea.style.display = '';
374
+ }
375
+ }, {
376
+ key: "inputAreaHide",
377
+ value: function inputAreaHide() {
378
+ this._inputArea.style.display = 'none';
379
+ }
380
+ }, {
381
+ key: "inputAreaSetEnabled",
382
+ value: function inputAreaSetEnabled(enabled) {
383
+ this._textEntry.disabled = !enabled;
384
+ this._sendButton.disabled = !enabled;
385
+ }
386
+ }, {
387
+ key: "inputAreaSetButtonText",
388
+ value: function inputAreaSetButtonText(text) {
389
+ this._sendButton.textContent = text;
390
+ }
391
+ }, {
392
+ key: "inputAreaGetButtonText",
393
+ value: function inputAreaGetButtonText() {
394
+ return this._sendButton.textContent;
395
+ }
396
+ }, {
397
+ key: "setDirection",
398
+ value: function setDirection(dir) {
399
+ var d = dir === 'rtl' ? 'rtl' : 'ltr';
400
+ this._chatWidget.setAttribute('dir', d);
401
+ if (d === 'rtl') {
402
+ this._chatWidget.classList.add('quikchat-rtl');
403
+ } else {
404
+ this._chatWidget.classList.remove('quikchat-rtl');
405
+ }
406
+ }
407
+ }, {
408
+ key: "getDirection",
409
+ value: function getDirection() {
410
+ return this._chatWidget.getAttribute('dir') || 'ltr';
411
+ }
412
+ }, {
413
+ key: "_handleContainerResize",
414
+ value: function _handleContainerResize() {
415
+ // Layout is handled by CSS flexbox — no JS height calculation needed.
416
+ // This hook exists for future use or custom resize callbacks.
417
+ }
418
+ }, {
419
+ key: "scrollToBottom",
420
+ value: function scrollToBottom() {
421
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
422
+ this.userScrolledUp = false;
423
+ this._updateScrollBottomBtn();
424
+ }
425
+ }, {
426
+ key: "_updateScrollBottomBtn",
427
+ value: function _updateScrollBottomBtn() {
428
+ if (this.userScrolledUp) {
429
+ this._scrollBottomBtn.classList.add('quikchat-scroll-bottom-visible');
430
+ } else {
431
+ this._scrollBottomBtn.classList.remove('quikchat-scroll-bottom-visible');
432
+ }
433
+ }
434
+ }, {
435
+ key: "_autoGrowTextarea",
436
+ value: function _autoGrowTextarea() {
437
+ var el = this._textEntry;
438
+ el.style.height = 'auto';
439
+ var maxHeight = parseInt(getComputedStyle(el).getPropertyValue('--quikchat-input-max-height')) || 120;
440
+ el.style.height = Math.min(el.scrollHeight, maxHeight) + 'px';
441
+ el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';
442
+ }
443
+ }, {
444
+ key: "_formatTimestamp",
445
+ value: function _formatTimestamp(isoString) {
446
+ var d = new Date(isoString);
447
+ var h = d.getHours();
448
+ var m = String(d.getMinutes()).padStart(2, '0');
449
+ var ampm = h >= 12 ? 'PM' : 'AM';
450
+ var h12 = h % 12 || 12;
451
+ return h12 + ':' + m + ' ' + ampm;
452
+ }
453
+ }, {
454
+ key: "messagesAreaShowTimestamps",
455
+ value: function messagesAreaShowTimestamps(show) {
456
+ if (show) {
457
+ this._messagesArea.classList.add('quikchat-show-timestamps');
458
+ } else {
459
+ this._messagesArea.classList.remove('quikchat-show-timestamps');
460
+ }
461
+ }
462
+ }, {
463
+ key: "messagesAreaShowTimestampsGet",
464
+ value: function messagesAreaShowTimestampsGet() {
465
+ return this._messagesArea.classList.contains('quikchat-show-timestamps');
466
+ }
467
+ }, {
468
+ key: "messagesAreaShowTimestampsToggle",
469
+ value: function messagesAreaShowTimestampsToggle() {
470
+ this._messagesArea.classList.toggle('quikchat-show-timestamps');
471
+ }
472
+ }, {
473
+ key: "_escapeHTML",
474
+ value: function _escapeHTML(str) {
475
+ var div = document.createElement('div');
476
+ div.textContent = str;
477
+ return div.innerHTML;
478
+ }
479
+ }, {
480
+ key: "_processContent",
481
+ value: function _processContent(content) {
482
+ if (this._sanitize === true) {
483
+ content = this._escapeHTML(content);
484
+ } else if (typeof this._sanitize === 'function') {
485
+ content = this._sanitize(content);
486
+ }
487
+ if (this._messageFormatter) {
488
+ content = this._messageFormatter(content);
489
+ }
490
+ return content;
491
+ }
492
+
493
+ //messagesArea functions
494
+ }, {
495
+ key: "messagesAreaAlternateColors",
496
+ value: function messagesAreaAlternateColors() {
497
+ var alt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
498
+ if (alt) {
499
+ this._messagesArea.classList.add('quikchat-messages-area-alt');
500
+ } else {
501
+ this._messagesArea.classList.remove('quikchat-messages-area-alt');
502
+ }
503
+ return alt === true;
504
+ }
505
+ }, {
506
+ key: "messagesAreaAlternateColorsToggle",
507
+ value: function messagesAreaAlternateColorsToggle() {
508
+ this._messagesArea.classList.toggle('quikchat-messages-area-alt');
509
+ }
510
+ }, {
511
+ key: "messagesAreaAlternateColorsGet",
512
+ value: function messagesAreaAlternateColorsGet() {
513
+ return this._messagesArea.classList.contains('quikchat-messages-area-alt');
514
+ }
515
+ // message functions
516
+ }, {
517
+ key: "messageAddFull",
518
+ value: function messageAddFull() {
519
+ var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
520
+ content: "",
521
+ userString: "user",
522
+ align: "right",
523
+ role: "user",
524
+ userID: -1
525
+ };
526
+ var msgid = this.msgid;
527
+ var messageDiv = document.createElement('div');
528
+ var msgidClass = 'quikchat-msgid-' + String(msgid).padStart(10, '0');
529
+ messageDiv.classList.add('quikchat-message', msgidClass);
530
+ messageDiv.classList.add('quikchat-role-' + (input.role || 'user'));
531
+ messageDiv.classList.add('quikchat-align-' + (input.align || 'right'));
532
+ this.msgid++;
533
+ messageDiv.classList.add(this._messagesArea.children.length % 2 === 1 ? 'quikchat-message-1' : 'quikchat-message-2');
534
+
535
+ // Visibility: default true, hidden messages get display:none
536
+ var visible = input.visible !== false;
537
+ if (!visible) {
538
+ messageDiv.style.display = 'none';
539
+ }
540
+
541
+ // Tags: array of strings for group-based visibility control
542
+ var tags = Array.isArray(input.tags) ? input.tags.slice() : [];
543
+ var userDiv = document.createElement('div');
544
+ userDiv.classList.add('quikchat-user-label');
545
+ userDiv.style.textAlign = input.align;
546
+ userDiv.innerHTML = input.userString;
547
+ var contentDiv = document.createElement('div');
548
+ contentDiv.classList.add('quikchat-message-content');
549
+ contentDiv.style.textAlign = input.align;
550
+ contentDiv.innerHTML = this._processContent(input.content);
551
+ var timestamp = new Date().toISOString();
552
+ var timestampSpan = document.createElement('span');
553
+ timestampSpan.classList.add('quikchat-timestamp');
554
+ timestampSpan.textContent = this._formatTimestamp(timestamp);
555
+ messageDiv.appendChild(userDiv);
556
+ messageDiv.appendChild(contentDiv);
557
+ messageDiv.appendChild(timestampSpan);
558
+ this._messagesArea.appendChild(messageDiv);
559
+
560
+ // Scroll to the last message only if the user is not actively scrolling up
561
+ if (!this.userScrolledUp) {
562
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
563
+ }
564
+ this._textEntry.value = '';
565
+ this._autoGrowTextarea();
566
+ var updatedtime = timestamp;
567
+ if (this.trackHistory) {
568
+ this._history.push(_objectSpread2(_objectSpread2({
569
+ msgid: msgid
570
+ }, input), {}, {
571
+ visible: visible,
572
+ tags: tags,
573
+ timestamp: timestamp,
574
+ updatedtime: updatedtime,
575
+ messageDiv: messageDiv
576
+ }));
577
+ if (this._history.length > this._historyLimit) {
578
+ this._history.shift();
579
+ }
580
+ }
581
+ if (this._onMessageAdded) {
582
+ this._onMessageAdded(this, msgid);
583
+ }
584
+ return msgid;
585
+ }
586
+ }, {
587
+ key: "messageAddNew",
588
+ value: function messageAddNew() {
589
+ var content = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
590
+ var userString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "user";
591
+ var align = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "right";
592
+ var role = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "user";
593
+ return this.messageAddFull({
594
+ content: content,
595
+ userString: userString,
596
+ align: align,
597
+ role: role
598
+ });
599
+ }
600
+ }, {
601
+ key: "messageRemove",
602
+ value: function messageRemove(n) {
603
+ var success = false;
604
+ try {
605
+ this._messagesArea.removeChild(this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))));
606
+ success = true;
607
+ } catch (_error) {
608
+ // Message ID not found
609
+ }
610
+ if (success) {
611
+ this._history.splice(this._history.findIndex(function (item) {
612
+ return item.msgid === n;
613
+ }), 1);
614
+ if (this._onMessageDelete) {
615
+ this._onMessageDelete(this, n);
616
+ }
617
+ }
618
+ return success;
619
+ }
620
+ /* returns the message html object from the DOM
621
+ */
622
+ }, {
623
+ key: "messageGetDOMObject",
624
+ value: function messageGetDOMObject(n) {
625
+ var msg = null;
626
+ try {
627
+ msg = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
628
+ } catch (_error) {
629
+ // Message ID not found
630
+ }
631
+ return msg;
632
+ }
633
+ /* returns the message content only
634
+ */
635
+ }, {
636
+ key: "messageGetContent",
637
+ value: function messageGetContent(n) {
638
+ var content = "";
639
+ try {
640
+ content = this._history.filter(function (item) {
641
+ return item.msgid === n;
642
+ })[0].content;
643
+ } catch (_error) {
644
+ // Message ID not found
645
+ }
646
+ return content;
647
+ }
648
+ }, {
649
+ key: "messageSetVisible",
650
+ value: function messageSetVisible(n, visible) {
651
+ var msgEl = this.messageGetDOMObject(n);
652
+ if (!msgEl) return false;
653
+ msgEl.style.display = visible ? '' : 'none';
654
+ var item = this._history.find(function (entry) {
655
+ return entry.msgid === n;
656
+ });
657
+ if (item) item.visible = visible;
658
+ return true;
659
+ }
660
+ }, {
661
+ key: "messageGetVisible",
662
+ value: function messageGetVisible(n) {
663
+ var item = this._history.find(function (entry) {
664
+ return entry.msgid === n;
665
+ });
666
+ return item ? item.visible !== false : false;
667
+ }
668
+ }, {
669
+ key: "messageToggleVisible",
670
+ value: function messageToggleVisible(n) {
671
+ var current = this.messageGetVisible(n);
672
+ return this.messageSetVisible(n, !current);
673
+ }
674
+ }, {
675
+ key: "messageSetVisibleByTag",
676
+ value: function messageSetVisibleByTag(tag, visible) {
677
+ var count = 0;
678
+ var _iterator = _createForOfIteratorHelper(this._history),
679
+ _step;
680
+ try {
681
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
682
+ var item = _step.value;
683
+ if (item.tags && item.tags.includes(tag)) {
684
+ this.messageSetVisible(item.msgid, visible);
685
+ count++;
686
+ }
687
+ }
688
+ } catch (err) {
689
+ _iterator.e(err);
690
+ } finally {
691
+ _iterator.f();
692
+ }
693
+ return count;
694
+ }
695
+ }, {
696
+ key: "messageGetTags",
697
+ value: function messageGetTags(n) {
698
+ var item = this._history.find(function (entry) {
699
+ return entry.msgid === n;
700
+ });
701
+ return item && item.tags ? item.tags.slice() : [];
702
+ }
703
+ }, {
704
+ key: "messageSetTags",
705
+ value: function messageSetTags(n, tags) {
706
+ var item = this._history.find(function (entry) {
707
+ return entry.msgid === n;
708
+ });
709
+ if (!item) return false;
710
+ item.tags = Array.isArray(tags) ? tags.slice() : [];
711
+ return true;
712
+ }
713
+
714
+ /* append message to the message content
715
+ */
716
+ }, {
717
+ key: "messageAppendContent",
718
+ value: function messageAppendContent(n, content) {
719
+ var success = false;
720
+ try {
721
+ var msgEl = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
722
+ var item = this._history.filter(function (entry) {
723
+ return entry.msgid === n;
724
+ })[0];
725
+ item.content += content;
726
+ item.updatedtime = new Date().toISOString();
727
+ msgEl.querySelector('.quikchat-message-content').innerHTML = this._processContent(item.content);
728
+ msgEl.classList.remove('quikchat-typing');
729
+ success = true;
730
+ if (!this.userScrolledUp) {
731
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
732
+ }
733
+ if (this._onMessageAppend) {
734
+ this._onMessageAppend(this, n, content);
735
+ }
736
+ } catch (_error) {
737
+ // Message ID not found
738
+ }
739
+ return success;
740
+ }
741
+
742
+ /* replace message content
743
+ */
744
+ }, {
745
+ key: "messageReplaceContent",
746
+ value: function messageReplaceContent(n, content) {
747
+ var success = false;
748
+ try {
749
+ var msgEl = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
750
+ var item = this._history.filter(function (entry) {
751
+ return entry.msgid === n;
752
+ })[0];
753
+ item.content = content;
754
+ item.updatedtime = new Date().toISOString();
755
+ msgEl.querySelector('.quikchat-message-content').innerHTML = this._processContent(content);
756
+ msgEl.classList.remove('quikchat-typing');
757
+ success = true;
758
+ if (!this.userScrolledUp) {
759
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
760
+ }
761
+ if (this._onMessageReplace) {
762
+ this._onMessageReplace(this, n, content);
763
+ }
764
+ } catch (_error) {
765
+ // Message ID not found
766
+ }
767
+ return success;
768
+ }
769
+ }, {
770
+ key: "messageAddTypingIndicator",
771
+ value: function messageAddTypingIndicator() {
772
+ var userString = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
773
+ var align = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'left';
774
+ var msgid = this.messageAddFull({
775
+ content: '',
776
+ userString: userString,
777
+ align: align,
778
+ role: 'assistant'
779
+ });
780
+ var msgEl = this.messageGetDOMObject(msgid);
781
+ msgEl.classList.add('quikchat-typing');
782
+ var contentDiv = msgEl.querySelector('.quikchat-message-content');
783
+ contentDiv.innerHTML = '<span class="quikchat-typing-dots"><span>.</span><span>.</span><span>.</span></span>';
784
+ return msgid;
785
+ }
786
+ }, {
787
+ key: "setMessageFormatter",
788
+ value: function setMessageFormatter(formatter) {
789
+ this._messageFormatter = formatter;
790
+ }
791
+ }, {
792
+ key: "setSanitize",
793
+ value: function setSanitize(sanitize) {
794
+ this._sanitize = sanitize;
795
+ }
796
+
797
+ // history functions
798
+ /**
799
+ *
800
+ * @param {*} n
801
+ * @param {*} m
802
+ * @returns array of history messages
803
+ */
804
+ }, {
805
+ key: "historyGet",
806
+ value: function historyGet(n, m) {
807
+ if (n === undefined) {
808
+ return this._history.slice();
809
+ }
810
+ if (m === undefined) {
811
+ if (n < 0) {
812
+ return this._history.slice(n);
813
+ }
814
+ return this._history.slice(n, n + 1);
815
+ }
816
+ return this._history.slice(n, m);
817
+ }
818
+ }, {
819
+ key: "historyClear",
820
+ value: function historyClear() {
821
+ this.msgid = 0;
822
+ this._history = [];
823
+ }
824
+ }, {
825
+ key: "historyGetLength",
826
+ value: function historyGetLength() {
827
+ return this._history.length;
828
+ }
829
+ }, {
830
+ key: "historyGetMessage",
831
+ value: function historyGetMessage(n) {
832
+ if (n >= 0 && n < this._history.length) {
833
+ return this._history[n];
834
+ }
835
+ return {};
836
+ }
837
+ }, {
838
+ key: "historyGetMessageContent",
839
+ value: function historyGetMessageContent(n) {
840
+ if (n >= 0 && n < this._history.length) {
841
+ return this._history[n].content;
842
+ }
843
+ return "";
844
+ }
845
+ }, {
846
+ key: "historyExport",
847
+ value: function historyExport() {
848
+ return this._history.map(function (item) {
849
+ return {
850
+ msgid: item.msgid,
851
+ content: item.content,
852
+ userString: item.userString,
853
+ align: item.align,
854
+ role: item.role,
855
+ userID: item.userID,
856
+ visible: item.visible,
857
+ tags: item.tags ? item.tags.slice() : [],
858
+ timestamp: item.timestamp,
859
+ updatedtime: item.updatedtime
860
+ };
861
+ });
862
+ }
863
+ }, {
864
+ key: "historyImport",
865
+ value: function historyImport(data) {
866
+ // Clear existing messages from DOM and history
867
+ this._messagesArea.innerHTML = '';
868
+ this._history = [];
869
+ this.msgid = 0;
870
+ var _iterator2 = _createForOfIteratorHelper(data),
871
+ _step2;
872
+ try {
873
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
874
+ var entry = _step2.value;
875
+ this.messageAddFull({
876
+ content: entry.content || '',
877
+ userString: entry.userString || 'user',
878
+ align: entry.align || 'right',
879
+ role: entry.role || 'user',
880
+ userID: entry.userID,
881
+ visible: entry.visible,
882
+ tags: entry.tags
883
+ });
884
+ }
885
+ } catch (err) {
886
+ _iterator2.e(err);
887
+ } finally {
888
+ _iterator2.f();
889
+ }
890
+ }
891
+ }, {
892
+ key: "changeTheme",
893
+ value: function changeTheme(newTheme) {
894
+ this._chatWidget.classList.remove(this._theme);
895
+ this._chatWidget.classList.add(newTheme);
896
+ this._theme = newTheme;
897
+ }
898
+ }, {
899
+ key: "theme",
900
+ get: function get() {
901
+ return this._theme;
902
+ }
903
+ }], [{
904
+ key: "version",
905
+ value: function version() {
906
+ return {
907
+ "version": "1.2.4",
908
+ "license": "BSD-2",
909
+ "url": "https://github/deftio/quikchat"
910
+ };
911
+ }
912
+
913
+ /**
914
+ * quikchat.loremIpsum() - Generate a simple string of Lorem Ipsum text (sample typographer's text) of numChars in length.
915
+ * borrowed from github.com/deftio/bitwrench.js
916
+ * @param {number} numChars - The number of characters to generate (random btw 25 and 150 if undefined).
917
+ * @param {number} [startSpot=0] - The starting index in the Lorem Ipsum text. If undefined, a random startSpot will be generated.
918
+ * @param {boolean} [startWithCapitalLetter=true] - If true, capitalize the first character or inject a capital letter if the first character isn't a capital letter.
919
+ *
920
+ * @returns {string} A string of Lorem Ipsum text.
921
+ *
922
+ * @example
923
+ * // Returns 200 characters of Lorem Ipsum starting from index 50
924
+ * loremIpsum(200, 50);
925
+ *
926
+ * @example
927
+ * //Returns a 200 Lorem Ipsum characters starting from a random index
928
+ * loremIpsum(200);
929
+ */
930
+ }, {
931
+ key: "loremIpsum",
932
+ value: function loremIpsum(numChars) {
933
+ var startSpot = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
934
+ var startWithCapitalLetter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
935
+ 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. ";
936
+ if (typeof numChars !== "number") {
937
+ numChars = Math.floor(Math.random() * 126) + 25;
938
+ }
939
+ if (startSpot === undefined) {
940
+ startSpot = Math.floor(Math.random() * loremText.length);
941
+ }
942
+ startSpot = startSpot % loremText.length;
943
+
944
+ // Move startSpot to the next non-whitespace and non-punctuation character
945
+ while (loremText[startSpot] === ' ' || /[.,:;!?]/.test(loremText[startSpot])) {
946
+ startSpot = (startSpot + 1) % loremText.length;
947
+ }
948
+ var l = loremText.substring(startSpot) + loremText.substring(0, startSpot);
949
+ var s = "";
950
+ while (numChars > 0) {
951
+ s += numChars < l.length ? l.substring(0, numChars) : l;
952
+ numChars -= l.length;
953
+ }
954
+ if (s[s.length - 1] === " ") {
955
+ s = s.substring(0, s.length - 1) + "."; // always end on non-whitespace. "." was chosen arbitrarily.
956
+ }
957
+ if (startWithCapitalLetter) {
958
+ s = s[0].toUpperCase() + s.substring(1);
959
+ }
960
+ return s;
961
+ }
962
+ }]);
963
+ }();
964
+
965
+ /**
966
+ * quikdown - Lightweight Markdown Parser
967
+ * @version 1.1.1
968
+ * @license BSD-2-Clause
969
+ * @copyright DeftIO 2025
970
+ */
971
+ /**
972
+ * quikdown - A minimal markdown parser optimized for chat/LLM output
973
+ * Supports tables, code blocks, lists, and common formatting
974
+ * @param {string} markdown - The markdown source text
975
+ * @param {Object} options - Optional configuration object
976
+ * @param {Function} options.fence_plugin - Custom renderer for fenced code blocks
977
+ * (content, fence_string) => html string
978
+ * @param {boolean} options.inline_styles - If true, uses inline styles instead of classes
979
+ * @param {boolean} options.bidirectional - If true, adds data-qd attributes for source tracking
980
+ * @param {boolean} options.lazy_linefeeds - If true, single newlines become <br> tags
981
+ * @returns {string} - The rendered HTML
982
+ */
983
+
984
+ // Version will be injected at build time
985
+ const quikdownVersion = '1.1.1';
986
+
987
+ // Constants for reuse
988
+ const CLASS_PREFIX = 'quikdown-';
989
+ const PLACEHOLDER_CB = '§CB';
990
+ const PLACEHOLDER_IC = '§IC';
991
+
992
+ // Escape map at module level
993
+ const ESC_MAP = {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'};
994
+
995
+ // Single source of truth for all style definitions - optimized
996
+ const QUIKDOWN_STYLES = {
997
+ h1: 'font-size:2em;font-weight:600;margin:.67em 0;text-align:left',
998
+ h2: 'font-size:1.5em;font-weight:600;margin:.83em 0',
999
+ h3: 'font-size:1.25em;font-weight:600;margin:1em 0',
1000
+ h4: 'font-size:1em;font-weight:600;margin:1.33em 0',
1001
+ h5: 'font-size:.875em;font-weight:600;margin:1.67em 0',
1002
+ h6: 'font-size:.85em;font-weight:600;margin:2em 0',
1003
+ pre: 'background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0',
1004
+ code: 'background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace',
1005
+ blockquote: 'border-left:4px solid #ddd;margin-left:0;padding-left:1em',
1006
+ table: 'border-collapse:collapse;width:100%;margin:1em 0',
1007
+ th: 'border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left',
1008
+ td: 'border:1px solid #ddd;padding:8px;text-align:left',
1009
+ hr: 'border:none;border-top:1px solid #ddd;margin:1em 0',
1010
+ img: 'max-width:100%;height:auto',
1011
+ a: 'color:#06c;text-decoration:underline',
1012
+ strong: 'font-weight:bold',
1013
+ em: 'font-style:italic',
1014
+ del: 'text-decoration:line-through',
1015
+ ul: 'margin:.5em 0;padding-left:2em',
1016
+ ol: 'margin:.5em 0;padding-left:2em',
1017
+ li: 'margin:.25em 0',
1018
+ // Task list specific styles
1019
+ 'task-item': 'list-style:none',
1020
+ 'task-checkbox': 'margin-right:.5em'
1021
+ };
1022
+
1023
+ // Factory function to create getAttr for a given context
1024
+ function createGetAttr(inline_styles, styles) {
1025
+ return function(tag, additionalStyle = '') {
1026
+ if (inline_styles) {
1027
+ let style = styles[tag];
1028
+ if (!style && !additionalStyle) return '';
1029
+
1030
+ // Remove default text-align if we're adding a different alignment
1031
+ if (additionalStyle && additionalStyle.includes('text-align') && style && style.includes('text-align')) {
1032
+ style = style.replace(/text-align:[^;]+;?/, '').trim();
1033
+ if (style && !style.endsWith(';')) style += ';';
1034
+ }
1035
+
1036
+ /* istanbul ignore next - defensive: additionalStyle without style doesn't occur with current tags */
1037
+ const fullStyle = additionalStyle ? (style ? `${style}${additionalStyle}` : additionalStyle) : style;
1038
+ return ` style="${fullStyle}"`;
1039
+ } else {
1040
+ const classAttr = ` class="${CLASS_PREFIX}${tag}"`;
1041
+ // Apply inline styles for alignment even when using CSS classes
1042
+ if (additionalStyle) {
1043
+ return `${classAttr} style="${additionalStyle}"`;
1044
+ }
1045
+ return classAttr;
1046
+ }
1047
+ };
1048
+ }
1049
+
1050
+ function quikdown(markdown, options = {}) {
1051
+ if (!markdown || typeof markdown !== 'string') {
1052
+ return '';
1053
+ }
1054
+
1055
+ const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false } = options;
1056
+ const styles = QUIKDOWN_STYLES; // Use module-level styles
1057
+ const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once
1058
+
1059
+ // Escape HTML entities to prevent XSS
1060
+ function escapeHtml(text) {
1061
+ return text.replace(/[&<>"']/g, m => ESC_MAP[m]);
1062
+ }
1063
+
1064
+ // Helper to add data-qd attributes for bidirectional support
1065
+ const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
1066
+
1067
+ // Sanitize URLs to prevent XSS attacks
1068
+ function sanitizeUrl(url, allowUnsafe = false) {
1069
+ /* istanbul ignore next - defensive programming, regex ensures url is never empty */
1070
+ if (!url) return '';
1071
+
1072
+ // If unsafe URLs are explicitly allowed, return as-is
1073
+ if (allowUnsafe) return url;
1074
+
1075
+ const trimmedUrl = url.trim();
1076
+ const lowerUrl = trimmedUrl.toLowerCase();
1077
+
1078
+ // Block dangerous protocols
1079
+ const dangerousProtocols = ['javascript:', 'vbscript:', 'data:'];
1080
+
1081
+ for (const protocol of dangerousProtocols) {
1082
+ if (lowerUrl.startsWith(protocol)) {
1083
+ // Exception: Allow data:image/* for images
1084
+ if (protocol === 'data:' && lowerUrl.startsWith('data:image/')) {
1085
+ return trimmedUrl;
1086
+ }
1087
+ // Return safe empty link for dangerous protocols
1088
+ return '#';
1089
+ }
1090
+ }
1091
+
1092
+ return trimmedUrl;
1093
+ }
1094
+
1095
+ // Process the markdown in phases
1096
+ let html = markdown;
1097
+
1098
+ // Phase 1: Extract and protect code blocks and inline code
1099
+ const codeBlocks = [];
1100
+ const inlineCodes = [];
1101
+
1102
+ // Extract fenced code blocks first (supports both ``` and ~~~)
1103
+ // Match paired fences - ``` with ``` and ~~~ with ~~~
1104
+ // Fence must be at start of line
1105
+ html = html.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm, (match, fence, lang, code) => {
1106
+ const placeholder = `${PLACEHOLDER_CB}${codeBlocks.length}§`;
1107
+
1108
+ // Trim the language specification
1109
+ const langTrimmed = lang ? lang.trim() : '';
1110
+
1111
+ // If custom fence plugin is provided, use it (v1.1.0: object format required)
1112
+ if (fence_plugin && fence_plugin.render && typeof fence_plugin.render === 'function') {
1113
+ codeBlocks.push({
1114
+ lang: langTrimmed,
1115
+ code: code.trimEnd(),
1116
+ custom: true,
1117
+ fence: fence,
1118
+ hasReverse: !!fence_plugin.reverse
1119
+ });
1120
+ } else {
1121
+ codeBlocks.push({
1122
+ lang: langTrimmed,
1123
+ code: escapeHtml(code.trimEnd()),
1124
+ custom: false,
1125
+ fence: fence
1126
+ });
1127
+ }
1128
+ return placeholder;
1129
+ });
1130
+
1131
+ // Extract inline code
1132
+ html = html.replace(/`([^`]+)`/g, (match, code) => {
1133
+ const placeholder = `${PLACEHOLDER_IC}${inlineCodes.length}§`;
1134
+ inlineCodes.push(escapeHtml(code));
1135
+ return placeholder;
1136
+ });
1137
+
1138
+ // Now escape HTML in the rest of the content
1139
+ html = escapeHtml(html);
1140
+
1141
+ // Phase 2: Process block elements
1142
+
1143
+ // Process tables
1144
+ html = processTable(html, getAttr);
1145
+
1146
+ // Process headings (supports optional trailing #'s)
1147
+ html = html.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm, (match, hashes, content) => {
1148
+ const level = hashes.length;
1149
+ return `<h${level}${getAttr('h' + level)}${dataQd(hashes)}>${content}</h${level}>`;
1150
+ });
1151
+
1152
+ // Process blockquotes (must handle escaped > since we already escaped HTML)
1153
+ html = html.replace(/^&gt;\s+(.+)$/gm, `<blockquote${getAttr('blockquote')}>$1</blockquote>`);
1154
+ // Merge consecutive blockquotes
1155
+ html = html.replace(/<\/blockquote>\n<blockquote>/g, '\n');
1156
+
1157
+ // Process horizontal rules (allow trailing spaces)
1158
+ html = html.replace(/^---+\s*$/gm, `<hr${getAttr('hr')}>`);
1159
+
1160
+ // Process lists
1161
+ html = processLists(html, getAttr, inline_styles, bidirectional);
1162
+
1163
+ // Phase 3: Process inline elements
1164
+
1165
+ // Images (must come before links, with URL sanitization)
1166
+ html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => {
1167
+ const sanitizedSrc = sanitizeUrl(src, options.allow_unsafe_urls);
1168
+ const altAttr = bidirectional && alt ? ` data-qd-alt="${escapeHtml(alt)}"` : '';
1169
+ const srcAttr = bidirectional ? ` data-qd-src="${escapeHtml(src)}"` : '';
1170
+ return `<img${getAttr('img')} src="${sanitizedSrc}" alt="${alt}"${altAttr}${srcAttr}${dataQd('!')}>`;
1171
+ });
1172
+
1173
+ // Links (with URL sanitization)
1174
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, href) => {
1175
+ // Sanitize URL to prevent XSS
1176
+ const sanitizedHref = sanitizeUrl(href, options.allow_unsafe_urls);
1177
+ const isExternal = /^https?:\/\//i.test(sanitizedHref);
1178
+ const rel = isExternal ? ' rel="noopener noreferrer"' : '';
1179
+ const textAttr = bidirectional ? ` data-qd-text="${escapeHtml(text)}"` : '';
1180
+ return `<a${getAttr('a')} href="${sanitizedHref}"${rel}${textAttr}${dataQd('[')}>${text}</a>`;
1181
+ });
1182
+
1183
+ // Autolinks - convert bare URLs to clickable links
1184
+ html = html.replace(/(^|\s)(https?:\/\/[^\s<]+)/g, (match, prefix, url) => {
1185
+ const sanitizedUrl = sanitizeUrl(url, options.allow_unsafe_urls);
1186
+ return `${prefix}<a${getAttr('a')} href="${sanitizedUrl}" rel="noopener noreferrer">${url}</a>`;
1187
+ });
1188
+
1189
+ // Process inline formatting (bold, italic, strikethrough)
1190
+ const inlinePatterns = [
1191
+ [/\*\*(.+?)\*\*/g, 'strong', '**'],
1192
+ [/__(.+?)__/g, 'strong', '__'],
1193
+ [/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em', '*'],
1194
+ [/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em', '_'],
1195
+ [/~~(.+?)~~/g, 'del', '~~']
1196
+ ];
1197
+
1198
+ inlinePatterns.forEach(([pattern, tag, marker]) => {
1199
+ html = html.replace(pattern, `<${tag}${getAttr(tag)}${dataQd(marker)}>$1</${tag}>`);
1200
+ });
1201
+
1202
+ // Line breaks
1203
+ if (lazy_linefeeds) {
1204
+ // Lazy linefeeds: single newline becomes <br> (except between paragraphs and after/before block elements)
1205
+ const blocks = [];
1206
+ let bi = 0;
1207
+
1208
+ // Protect tables and lists
1209
+ html = html.replace(/<(table|[uo]l)[^>]*>[\s\S]*?<\/\1>/g, m => {
1210
+ blocks[bi] = m;
1211
+ return `§B${bi++}§`;
1212
+ });
1213
+
1214
+ // Handle paragraphs and block elements
1215
+ html = html.replace(/\n\n+/g, '§P§')
1216
+ // After block elements
1217
+ .replace(/(<\/(?:h[1-6]|blockquote|pre)>)\n/g, '$1§N§')
1218
+ .replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\n/g, '$1§N§')
1219
+ // Before block elements
1220
+ .replace(/\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g, '§N§$1')
1221
+ .replace(/\n(§B\d+§)/g, '§N§$1')
1222
+ .replace(/(§B\d+§)\n/g, '$1§N§')
1223
+ // Convert remaining newlines
1224
+ .replace(/\n/g, `<br${getAttr('br')}>`)
1225
+ // Restore
1226
+ .replace(/§N§/g, '\n')
1227
+ .replace(/§P§/g, '</p><p>');
1228
+
1229
+ // Restore protected blocks
1230
+ blocks.forEach((b, i) => html = html.replace(`§B${i}§`, b));
1231
+
1232
+ html = '<p>' + html + '</p>';
1233
+ } else {
1234
+ // Standard: two spaces at end of line for line breaks
1235
+ html = html.replace(/ $/gm, `<br${getAttr('br')}>`);
1236
+
1237
+ // Paragraphs (double newlines)
1238
+ // Don't add </p> after block elements (they're not in paragraphs)
1239
+ html = html.replace(/\n\n+/g, (match, offset) => {
1240
+ // Check if we're after a block element closing tag
1241
+ const before = html.substring(0, offset);
1242
+ if (before.match(/<\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)) {
1243
+ return '<p>'; // Just open a new paragraph
1244
+ }
1245
+ return '</p><p>'; // Normal paragraph break
1246
+ });
1247
+ html = '<p>' + html + '</p>';
1248
+ }
1249
+
1250
+ // Clean up empty paragraphs and unwrap block elements
1251
+ const cleanupPatterns = [
1252
+ [/<p><\/p>/g, ''],
1253
+ [/<p>(<h[1-6][^>]*>)/g, '$1'],
1254
+ [/(<\/h[1-6]>)<\/p>/g, '$1'],
1255
+ [/<p>(<blockquote[^>]*>)/g, '$1'],
1256
+ [/(<\/blockquote>)<\/p>/g, '$1'],
1257
+ [/<p>(<ul[^>]*>|<ol[^>]*>)/g, '$1'],
1258
+ [/(<\/ul>|<\/ol>)<\/p>/g, '$1'],
1259
+ [/<p>(<hr[^>]*>)<\/p>/g, '$1'],
1260
+ [/<p>(<table[^>]*>)/g, '$1'],
1261
+ [/(<\/table>)<\/p>/g, '$1'],
1262
+ [/<p>(<pre[^>]*>)/g, '$1'],
1263
+ [/(<\/pre>)<\/p>/g, '$1'],
1264
+ [new RegExp(`<p>(${PLACEHOLDER_CB}\\d+§)<\/p>`, 'g'), '$1']
1265
+ ];
1266
+
1267
+ cleanupPatterns.forEach(([pattern, replacement]) => {
1268
+ html = html.replace(pattern, replacement);
1269
+ });
1270
+
1271
+ // Fix orphaned closing </p> tags after block elements
1272
+ // When a paragraph follows a block element, ensure it has opening <p>
1273
+ html = html.replace(/(<\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\n([^<])/g, '$1\n<p>$2');
1274
+
1275
+ // Phase 4: Restore code blocks and inline code
1276
+
1277
+ // Restore code blocks
1278
+ codeBlocks.forEach((block, i) => {
1279
+ let replacement;
1280
+
1281
+ if (block.custom && fence_plugin && fence_plugin.render) {
1282
+ // Use custom fence plugin (v1.1.0: object format with render function)
1283
+ replacement = fence_plugin.render(block.code, block.lang);
1284
+
1285
+ // If plugin returns undefined, fall back to default rendering
1286
+ if (replacement === undefined) {
1287
+ const langClass = !inline_styles && block.lang ? ` class="language-${block.lang}"` : '';
1288
+ const codeAttr = inline_styles ? getAttr('code') : langClass;
1289
+ const langAttr = bidirectional && block.lang ? ` data-qd-lang="${escapeHtml(block.lang)}"` : '';
1290
+ const fenceAttr = bidirectional ? ` data-qd-fence="${escapeHtml(block.fence)}"` : '';
1291
+ replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${escapeHtml(block.code)}</code></pre>`;
1292
+ } else if (bidirectional) {
1293
+ // If bidirectional and plugin provided HTML, add data attributes for roundtrip
1294
+ replacement = replacement.replace(/^<(\w+)/,
1295
+ `<$1 data-qd-fence="${escapeHtml(block.fence)}" data-qd-lang="${escapeHtml(block.lang)}" data-qd-source="${escapeHtml(block.code)}"`);
1296
+ }
1297
+ } else {
1298
+ // Default rendering
1299
+ const langClass = !inline_styles && block.lang ? ` class="language-${block.lang}"` : '';
1300
+ const codeAttr = inline_styles ? getAttr('code') : langClass;
1301
+ const langAttr = bidirectional && block.lang ? ` data-qd-lang="${escapeHtml(block.lang)}"` : '';
1302
+ const fenceAttr = bidirectional ? ` data-qd-fence="${escapeHtml(block.fence)}"` : '';
1303
+ replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${block.code}</code></pre>`;
1304
+ }
1305
+
1306
+ const placeholder = `${PLACEHOLDER_CB}${i}§`;
1307
+ html = html.replace(placeholder, replacement);
1308
+ });
1309
+
1310
+ // Restore inline code
1311
+ inlineCodes.forEach((code, i) => {
1312
+ const placeholder = `${PLACEHOLDER_IC}${i}§`;
1313
+ html = html.replace(placeholder, `<code${getAttr('code')}${dataQd('`')}>${code}</code>`);
1314
+ });
1315
+
1316
+ return html.trim();
1317
+ }
1318
+
1319
+ /**
1320
+ * Process inline markdown formatting
1321
+ */
1322
+ function processInlineMarkdown(text, getAttr) {
1323
+
1324
+ // Process inline formatting patterns
1325
+ const patterns = [
1326
+ [/\*\*(.+?)\*\*/g, 'strong'],
1327
+ [/__(.+?)__/g, 'strong'],
1328
+ [/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em'],
1329
+ [/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],
1330
+ [/~~(.+?)~~/g, 'del'],
1331
+ [/`([^`]+)`/g, 'code']
1332
+ ];
1333
+
1334
+ patterns.forEach(([pattern, tag]) => {
1335
+ text = text.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);
1336
+ });
1337
+
1338
+ return text;
1339
+ }
1340
+
1341
+ /**
1342
+ * Process markdown tables
1343
+ */
1344
+ function processTable(text, getAttr) {
1345
+ const lines = text.split('\n');
1346
+ const result = [];
1347
+ let inTable = false;
1348
+ let tableLines = [];
1349
+
1350
+ for (let i = 0; i < lines.length; i++) {
1351
+ const line = lines[i].trim();
1352
+
1353
+ // Check if this line looks like a table row (with or without trailing |)
1354
+ if (line.includes('|') && (line.startsWith('|') || /[^\\|]/.test(line))) {
1355
+ if (!inTable) {
1356
+ inTable = true;
1357
+ tableLines = [];
1358
+ }
1359
+ tableLines.push(line);
1360
+ } else {
1361
+ // Not a table line
1362
+ if (inTable) {
1363
+ // Process the accumulated table
1364
+ const tableHtml = buildTable(tableLines, getAttr);
1365
+ if (tableHtml) {
1366
+ result.push(tableHtml);
1367
+ } else {
1368
+ // Not a valid table, restore original lines
1369
+ result.push(...tableLines);
1370
+ }
1371
+ inTable = false;
1372
+ tableLines = [];
1373
+ }
1374
+ result.push(lines[i]);
1375
+ }
1376
+ }
1377
+
1378
+ // Handle table at end of text
1379
+ if (inTable && tableLines.length > 0) {
1380
+ const tableHtml = buildTable(tableLines, getAttr);
1381
+ if (tableHtml) {
1382
+ result.push(tableHtml);
1383
+ } else {
1384
+ result.push(...tableLines);
1385
+ }
1386
+ }
1387
+
1388
+ return result.join('\n');
1389
+ }
1390
+
1391
+ /**
1392
+ * Build an HTML table from markdown table lines
1393
+ */
1394
+ function buildTable(lines, getAttr) {
1395
+
1396
+ if (lines.length < 2) return null;
1397
+
1398
+ // Check for separator line (second line should be the separator)
1399
+ let separatorIndex = -1;
1400
+ for (let i = 1; i < lines.length; i++) {
1401
+ // Support separator with or without leading/trailing pipes
1402
+ if (/^\|?[\s\-:|]+\|?$/.test(lines[i]) && lines[i].includes('-')) {
1403
+ separatorIndex = i;
1404
+ break;
1405
+ }
1406
+ }
1407
+
1408
+ if (separatorIndex === -1) return null;
1409
+
1410
+ const headerLines = lines.slice(0, separatorIndex);
1411
+ const bodyLines = lines.slice(separatorIndex + 1);
1412
+
1413
+ // Parse alignment from separator
1414
+ const separator = lines[separatorIndex];
1415
+ // Handle pipes at start/end or not
1416
+ const separatorCells = separator.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
1417
+ const alignments = separatorCells.map(cell => {
1418
+ const trimmed = cell.trim();
1419
+ if (trimmed.startsWith(':') && trimmed.endsWith(':')) return 'center';
1420
+ if (trimmed.endsWith(':')) return 'right';
1421
+ return 'left';
1422
+ });
1423
+
1424
+ let html = `<table${getAttr('table')}>\n`;
1425
+
1426
+ // Build header
1427
+ // Note: headerLines will always have length > 0 since separatorIndex starts from 1
1428
+ html += `<thead${getAttr('thead')}>\n`;
1429
+ headerLines.forEach(line => {
1430
+ html += `<tr${getAttr('tr')}>\n`;
1431
+ // Handle pipes at start/end or not
1432
+ const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
1433
+ cells.forEach((cell, i) => {
1434
+ const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';
1435
+ const processedCell = processInlineMarkdown(cell.trim(), getAttr);
1436
+ html += `<th${getAttr('th', alignStyle)}>${processedCell}</th>\n`;
1437
+ });
1438
+ html += '</tr>\n';
1439
+ });
1440
+ html += '</thead>\n';
1441
+
1442
+ // Build body
1443
+ if (bodyLines.length > 0) {
1444
+ html += `<tbody${getAttr('tbody')}>\n`;
1445
+ bodyLines.forEach(line => {
1446
+ html += `<tr${getAttr('tr')}>\n`;
1447
+ // Handle pipes at start/end or not
1448
+ const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
1449
+ cells.forEach((cell, i) => {
1450
+ const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';
1451
+ const processedCell = processInlineMarkdown(cell.trim(), getAttr);
1452
+ html += `<td${getAttr('td', alignStyle)}>${processedCell}</td>\n`;
1453
+ });
1454
+ html += '</tr>\n';
1455
+ });
1456
+ html += '</tbody>\n';
1457
+ }
1458
+
1459
+ html += '</table>';
1460
+ return html;
1461
+ }
1462
+
1463
+ /**
1464
+ * Process markdown lists (ordered and unordered)
1465
+ */
1466
+ function processLists(text, getAttr, inline_styles, bidirectional) {
1467
+
1468
+ const lines = text.split('\n');
1469
+ const result = [];
1470
+ let listStack = []; // Track nested lists
1471
+
1472
+ // Helper to escape HTML for data-qd attributes
1473
+ const escapeHtml = (text) => text.replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[m]);
1474
+ const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
1475
+
1476
+ for (let i = 0; i < lines.length; i++) {
1477
+ const line = lines[i];
1478
+ const match = line.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);
1479
+
1480
+ if (match) {
1481
+ const [, indent, marker, content] = match;
1482
+ const level = Math.floor(indent.length / 2);
1483
+ const isOrdered = /^\d+\./.test(marker);
1484
+ const listType = isOrdered ? 'ol' : 'ul';
1485
+
1486
+ // Check for task list items
1487
+ let listItemContent = content;
1488
+ let taskListClass = '';
1489
+ const taskMatch = content.match(/^\[([x ])\]\s+(.*)$/i);
1490
+ if (taskMatch && !isOrdered) {
1491
+ const [, checked, taskContent] = taskMatch;
1492
+ const isChecked = checked.toLowerCase() === 'x';
1493
+ const checkboxAttr = inline_styles
1494
+ ? ' style="margin-right:.5em"'
1495
+ : ` class="${CLASS_PREFIX}task-checkbox"`;
1496
+ listItemContent = `<input type="checkbox"${checkboxAttr}${isChecked ? ' checked' : ''} disabled> ${taskContent}`;
1497
+ taskListClass = inline_styles ? ' style="list-style:none"' : ` class="${CLASS_PREFIX}task-item"`;
1498
+ }
1499
+
1500
+ // Close deeper levels
1501
+ while (listStack.length > level + 1) {
1502
+ const list = listStack.pop();
1503
+ result.push(`</${list.type}>`);
1504
+ }
1505
+
1506
+ // Open new level if needed
1507
+ if (listStack.length === level) {
1508
+ // Need to open a new list
1509
+ listStack.push({ type: listType, level });
1510
+ result.push(`<${listType}${getAttr(listType)}>`);
1511
+ } else if (listStack.length === level + 1) {
1512
+ // Check if we need to switch list type
1513
+ const currentList = listStack[listStack.length - 1];
1514
+ if (currentList.type !== listType) {
1515
+ result.push(`</${currentList.type}>`);
1516
+ listStack.pop();
1517
+ listStack.push({ type: listType, level });
1518
+ result.push(`<${listType}${getAttr(listType)}>`);
1519
+ }
1520
+ }
1521
+
1522
+ const liAttr = taskListClass || getAttr('li');
1523
+ result.push(`<li${liAttr}${dataQd(marker)}>${listItemContent}</li>`);
1524
+ } else {
1525
+ // Not a list item, close all lists
1526
+ while (listStack.length > 0) {
1527
+ const list = listStack.pop();
1528
+ result.push(`</${list.type}>`);
1529
+ }
1530
+ result.push(line);
1531
+ }
1532
+ }
1533
+
1534
+ // Close any remaining lists
1535
+ while (listStack.length > 0) {
1536
+ const list = listStack.pop();
1537
+ result.push(`</${list.type}>`);
1538
+ }
1539
+
1540
+ return result.join('\n');
1541
+ }
1542
+
1543
+ /**
1544
+ * Emit CSS styles for quikdown elements
1545
+ * @param {string} prefix - Optional class prefix (default: 'quikdown-')
1546
+ * @param {string} theme - Optional theme: 'light' (default) or 'dark'
1547
+ * @returns {string} CSS string with quikdown styles
1548
+ */
1549
+ quikdown.emitStyles = function(prefix = 'quikdown-', theme = 'light') {
1550
+ const styles = QUIKDOWN_STYLES;
1551
+
1552
+ // Define theme color overrides
1553
+ const themeOverrides = {
1554
+ dark: {
1555
+ '#f4f4f4': '#2a2a2a', // pre background
1556
+ '#f0f0f0': '#2a2a2a', // code background
1557
+ '#f2f2f2': '#2a2a2a', // th background
1558
+ '#ddd': '#3a3a3a', // borders
1559
+ '#06c': '#6db3f2', // links
1560
+ _textColor: '#e0e0e0'
1561
+ },
1562
+ light: {
1563
+ _textColor: '#333' // Explicit text color for light theme
1564
+ }
1565
+ };
1566
+
1567
+ let css = '';
1568
+ for (const [tag, style] of Object.entries(styles)) {
1569
+ let themedStyle = style;
1570
+
1571
+ // Apply theme overrides if dark theme
1572
+ if (theme === 'dark' && themeOverrides.dark) {
1573
+ // Replace colors
1574
+ for (const [oldColor, newColor] of Object.entries(themeOverrides.dark)) {
1575
+ if (!oldColor.startsWith('_')) {
1576
+ themedStyle = themedStyle.replace(new RegExp(oldColor, 'g'), newColor);
1577
+ }
1578
+ }
1579
+
1580
+ // Add text color for certain elements in dark theme
1581
+ const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];
1582
+ if (needsTextColor.includes(tag)) {
1583
+ themedStyle += `;color:${themeOverrides.dark._textColor}`;
1584
+ }
1585
+ } else if (theme === 'light' && themeOverrides.light) {
1586
+ // Add explicit text color for light theme elements too
1587
+ const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];
1588
+ if (needsTextColor.includes(tag)) {
1589
+ themedStyle += `;color:${themeOverrides.light._textColor}`;
1590
+ }
1591
+ }
1592
+
1593
+ css += `.${prefix}${tag} { ${themedStyle} }\n`;
1594
+ }
1595
+
1596
+ return css;
1597
+ };
1598
+
1599
+ /**
1600
+ * Configure quikdown with options and return a function
1601
+ * @param {Object} options - Configuration options
1602
+ * @returns {Function} Configured quikdown function
1603
+ */
1604
+ quikdown.configure = function(options) {
1605
+ return function(markdown) {
1606
+ return quikdown(markdown, options);
1607
+ };
1608
+ };
1609
+
1610
+ /**
1611
+ * Version information
1612
+ */
1613
+ quikdown.version = quikdownVersion;
1614
+
1615
+ // Export for both CommonJS and ES6
1616
+ /* istanbul ignore next */
1617
+ if (typeof module !== 'undefined' && module.exports) {
1618
+ module.exports = quikdown;
1619
+ }
1620
+
1621
+ // For browser global
1622
+ /* istanbul ignore next */
1623
+ if (typeof window !== 'undefined') {
1624
+ window.quikdown = quikdown;
1625
+ }
1626
+
1627
+ // Subclass that pre-wires quikdown as the message formatter
1628
+ var quikchatMD = /*#__PURE__*/function (_quikchat) {
1629
+ function quikchatMD(parentElement, onSend) {
1630
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1631
+ _classCallCheck(this, quikchatMD);
1632
+ if (!options.messageFormatter) {
1633
+ options.messageFormatter = function (content) {
1634
+ return quikdown(content);
1635
+ };
1636
+ }
1637
+ return _callSuper(this, quikchatMD, [parentElement, onSend, options]);
1638
+ }
1639
+ _inherits(quikchatMD, _quikchat);
1640
+ return _createClass(quikchatMD);
1641
+ }(quikchat); // Expose quikdown on the class for direct access
1642
+ quikchatMD.quikdown = quikdown;
1643
+
1644
+ return quikchatMD;
1645
+
1646
+ }));
1647
+ //# sourceMappingURL=quikchat-md.umd.js.map