PluginProbe ʕ •ᴥ•ʔ
Cost Calculator for Elementor / 1.2.8
Cost Calculator for Elementor v1.2.8
trunk 1.2.8 1.2.9 1.3.0 1.3.1 1.3.5 1.4.0
cost-calculator-for-elementor / libs / tribute / tribute.js
cost-calculator-for-elementor / libs / tribute Last commit date
tribute.css 2 years ago tribute.esm.js 2 years ago tribute.js 2 years ago tribute.min.js 2 years ago tribute.min.js.map 2 years ago
tribute.js
1855 lines
1 (function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global = global || self, global.Tribute = factory());
5 }(this, (function () { 'use strict';
6
7 function _classCallCheck(instance, Constructor) {
8 if (!(instance instanceof Constructor)) {
9 throw new TypeError("Cannot call a class as a function");
10 }
11 }
12
13 function _defineProperties(target, props) {
14 for (var i = 0; i < props.length; i++) {
15 var descriptor = props[i];
16 descriptor.enumerable = descriptor.enumerable || false;
17 descriptor.configurable = true;
18 if ("value" in descriptor) descriptor.writable = true;
19 Object.defineProperty(target, descriptor.key, descriptor);
20 }
21 }
22
23 function _createClass(Constructor, protoProps, staticProps) {
24 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
25 if (staticProps) _defineProperties(Constructor, staticProps);
26 return Constructor;
27 }
28
29 function _slicedToArray(arr, i) {
30 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
31 }
32
33 function _arrayWithHoles(arr) {
34 if (Array.isArray(arr)) return arr;
35 }
36
37 function _iterableToArrayLimit(arr, i) {
38 if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
39 var _arr = [];
40 var _n = true;
41 var _d = false;
42 var _e = undefined;
43
44 try {
45 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
46 _arr.push(_s.value);
47
48 if (i && _arr.length === i) break;
49 }
50 } catch (err) {
51 _d = true;
52 _e = err;
53 } finally {
54 try {
55 if (!_n && _i["return"] != null) _i["return"]();
56 } finally {
57 if (_d) throw _e;
58 }
59 }
60
61 return _arr;
62 }
63
64 function _unsupportedIterableToArray(o, minLen) {
65 if (!o) return;
66 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
67 var n = Object.prototype.toString.call(o).slice(8, -1);
68 if (n === "Object" && o.constructor) n = o.constructor.name;
69 if (n === "Map" || n === "Set") return Array.from(n);
70 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
71 }
72
73 function _arrayLikeToArray(arr, len) {
74 if (len == null || len > arr.length) len = arr.length;
75
76 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
77
78 return arr2;
79 }
80
81 function _nonIterableRest() {
82 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
83 }
84
85 if (!Array.prototype.find) {
86 Array.prototype.find = function (predicate) {
87 if (this === null) {
88 throw new TypeError('Array.prototype.find called on null or undefined');
89 }
90
91 if (typeof predicate !== 'function') {
92 throw new TypeError('predicate must be a function');
93 }
94
95 var list = Object(this);
96 var length = list.length >>> 0;
97 var thisArg = arguments[1];
98 var value;
99
100 for (var i = 0; i < length; i++) {
101 value = list[i];
102
103 if (predicate.call(thisArg, value, i, list)) {
104 return value;
105 }
106 }
107
108 return undefined;
109 };
110 }
111
112 if (window && typeof window.CustomEvent !== "function") {
113 var CustomEvent$1 = function CustomEvent(event, params) {
114 params = params || {
115 bubbles: false,
116 cancelable: false,
117 detail: undefined
118 };
119 var evt = document.createEvent('CustomEvent');
120 evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
121 return evt;
122 };
123
124 if (typeof window.Event !== 'undefined') {
125 CustomEvent$1.prototype = window.Event.prototype;
126 }
127
128 window.CustomEvent = CustomEvent$1;
129 }
130
131 var TributeEvents = /*#__PURE__*/function () {
132 function TributeEvents(tribute) {
133 _classCallCheck(this, TributeEvents);
134
135 this.tribute = tribute;
136 this.tribute.events = this;
137 }
138
139 _createClass(TributeEvents, [{
140 key: "bind",
141 value: function bind(element) {
142 element.boundKeydown = this.keydown.bind(element, this);
143 element.boundKeyup = this.keyup.bind(element, this);
144 element.boundInput = this.input.bind(element, this);
145 element.addEventListener("keydown", element.boundKeydown, false);
146 element.addEventListener("keyup", element.boundKeyup, false);
147 element.addEventListener("input", element.boundInput, false);
148 }
149 }, {
150 key: "unbind",
151 value: function unbind(element) {
152 element.removeEventListener("keydown", element.boundKeydown, false);
153 element.removeEventListener("keyup", element.boundKeyup, false);
154 element.removeEventListener("input", element.boundInput, false);
155 delete element.boundKeydown;
156 delete element.boundKeyup;
157 delete element.boundInput;
158 }
159 }, {
160 key: "keydown",
161 value: function keydown(instance, event) {
162 if (instance.shouldDeactivate(event)) {
163 instance.tribute.isActive = false;
164 instance.tribute.hideMenu();
165 }
166
167 var element = this;
168 instance.commandEvent = false;
169 TributeEvents.keys().forEach(function (o) {
170 if (o.key === event.keyCode) {
171 instance.commandEvent = true;
172 instance.callbacks()[o.value.toLowerCase()](event, element);
173 }
174 });
175 }
176 }, {
177 key: "input",
178 value: function input(instance, event) {
179 instance.inputEvent = true;
180 instance.keyup.call(this, instance, event);
181 }
182 }, {
183 key: "click",
184 value: function click(instance, event) {
185 var tribute = instance.tribute;
186
187 if (tribute.menu && tribute.menu.contains(event.target)) {
188 var li = event.target;
189 event.preventDefault();
190 event.stopPropagation();
191
192 while (li.nodeName.toLowerCase() !== "li") {
193 li = li.parentNode;
194
195 if (!li || li === tribute.menu) {
196 throw new Error("cannot find the <li> container for the click");
197 }
198 }
199
200 tribute.selectItemAtIndex(li.getAttribute("data-index"), event);
201 tribute.hideMenu(); // TODO: should fire with externalTrigger and target is outside of menu
202 } else if (tribute.current.element && !tribute.current.externalTrigger) {
203 tribute.current.externalTrigger = false;
204 setTimeout(function () {
205 return tribute.hideMenu();
206 });
207 }
208 }
209 }, {
210 key: "keyup",
211 value: function keyup(instance, event) {
212 if (instance.inputEvent) {
213 instance.inputEvent = false;
214 }
215
216 instance.updateSelection(this);
217 if (event.keyCode === 27) return;
218
219 if (!instance.tribute.allowSpaces && instance.tribute.hasTrailingSpace) {
220 instance.tribute.hasTrailingSpace = false;
221 instance.commandEvent = true;
222 instance.callbacks()["space"](event, this);
223 return;
224 }
225
226 if (!instance.tribute.isActive) {
227 if (instance.tribute.autocompleteMode) {
228 instance.callbacks().triggerChar(event, this, "");
229 } else {
230 var keyCode = instance.getKeyCode(instance, this, event);
231 if (isNaN(keyCode) || !keyCode) return;
232 var trigger = instance.tribute.triggers().find(function (trigger) {
233 return trigger.charCodeAt(0) === keyCode;
234 });
235
236 if (typeof trigger !== "undefined") {
237 instance.callbacks().triggerChar(event, this, trigger);
238 }
239 }
240 }
241
242 if (instance.tribute.current.mentionText.length < instance.tribute.current.collection.menuShowMinLength) {
243 return;
244 }
245
246 if ((instance.tribute.current.trigger || instance.tribute.autocompleteMode) && instance.commandEvent === false || instance.tribute.isActive && event.keyCode === 8) {
247 instance.tribute.showMenuFor(this, true);
248 }
249 }
250 }, {
251 key: "shouldDeactivate",
252 value: function shouldDeactivate(event) {
253 if (!this.tribute.isActive) return false;
254
255 if (this.tribute.current.mentionText.length === 0) {
256 var eventKeyPressed = false;
257 TributeEvents.keys().forEach(function (o) {
258 if (event.keyCode === o.key) eventKeyPressed = true;
259 });
260 return !eventKeyPressed;
261 }
262
263 return false;
264 }
265 }, {
266 key: "getKeyCode",
267 value: function getKeyCode(instance, el, event) {
268
269 var tribute = instance.tribute;
270 var info = tribute.range.getTriggerInfo(false, tribute.hasTrailingSpace, true, tribute.allowSpaces, tribute.autocompleteMode);
271
272 if (info) {
273 return info.mentionTriggerChar.charCodeAt(0);
274 } else {
275 return false;
276 }
277 }
278 }, {
279 key: "updateSelection",
280 value: function updateSelection(el) {
281 this.tribute.current.element = el;
282 var info = this.tribute.range.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces, this.tribute.autocompleteMode);
283
284 if (info) {
285 this.tribute.current.selectedPath = info.mentionSelectedPath;
286 this.tribute.current.mentionText = info.mentionText;
287 this.tribute.current.selectedOffset = info.mentionSelectedOffset;
288 }
289 }
290 }, {
291 key: "callbacks",
292 value: function callbacks() {
293 var _this = this;
294
295 return {
296 triggerChar: function triggerChar(e, el, trigger) {
297 var tribute = _this.tribute;
298 tribute.current.trigger = trigger;
299 var collectionItem = tribute.collection.find(function (item) {
300 return item.trigger === trigger;
301 });
302 tribute.current.collection = collectionItem;
303
304 if (tribute.current.mentionText.length >= tribute.current.collection.menuShowMinLength && tribute.inputEvent) {
305 tribute.showMenuFor(el, true);
306 }
307 },
308 enter: function enter(e, el) {
309 // choose selection
310 if (_this.tribute.isActive && _this.tribute.current.filteredItems) {
311 e.preventDefault();
312 e.stopPropagation();
313 setTimeout(function () {
314 _this.tribute.selectItemAtIndex(_this.tribute.menuSelected, e);
315
316 _this.tribute.hideMenu();
317 }, 0);
318 }
319 },
320 escape: function escape(e, el) {
321 if (_this.tribute.isActive) {
322 e.preventDefault();
323 e.stopPropagation();
324 _this.tribute.isActive = false;
325
326 _this.tribute.hideMenu();
327 }
328 },
329 tab: function tab(e, el) {
330 // choose first match
331 _this.callbacks().enter(e, el);
332 },
333 space: function space(e, el) {
334 if (_this.tribute.isActive) {
335 if (_this.tribute.spaceSelectsMatch) {
336 _this.callbacks().enter(e, el);
337 } else if (!_this.tribute.allowSpaces) {
338 e.stopPropagation();
339 setTimeout(function () {
340 _this.tribute.hideMenu();
341
342 _this.tribute.isActive = false;
343 }, 0);
344 }
345 }
346 },
347 up: function up(e, el) {
348 // navigate up ul
349 if (_this.tribute.isActive && _this.tribute.current.filteredItems) {
350 e.preventDefault();
351 e.stopPropagation();
352 var count = _this.tribute.current.filteredItems.length,
353 selected = _this.tribute.menuSelected;
354
355 if (count > selected && selected > 0) {
356 _this.tribute.menuSelected--;
357
358 _this.setActiveLi();
359 } else if (selected === 0) {
360 _this.tribute.menuSelected = count - 1;
361
362 _this.setActiveLi();
363
364 _this.tribute.menu.scrollTop = _this.tribute.menu.scrollHeight;
365 }
366 }
367 },
368 down: function down(e, el) {
369 // navigate down ul
370 if (_this.tribute.isActive && _this.tribute.current.filteredItems) {
371 e.preventDefault();
372 e.stopPropagation();
373 var count = _this.tribute.current.filteredItems.length - 1,
374 selected = _this.tribute.menuSelected;
375
376 if (count > selected) {
377 _this.tribute.menuSelected++;
378
379 _this.setActiveLi();
380 } else if (count === selected) {
381 _this.tribute.menuSelected = 0;
382
383 _this.setActiveLi();
384
385 _this.tribute.menu.scrollTop = 0;
386 }
387 }
388 },
389 "delete": function _delete(e, el) {
390 if (_this.tribute.isActive && _this.tribute.current.mentionText.length < 1) {
391 _this.tribute.hideMenu();
392 } else if (_this.tribute.isActive) {
393 _this.tribute.showMenuFor(el);
394 }
395 }
396 };
397 }
398 }, {
399 key: "setActiveLi",
400 value: function setActiveLi(index) {
401 var lis = this.tribute.menu.querySelectorAll("li"),
402 length = lis.length >>> 0;
403 if (index) this.tribute.menuSelected = parseInt(index);
404
405 for (var i = 0; i < length; i++) {
406 var li = lis[i];
407
408 if (i === this.tribute.menuSelected) {
409 li.classList.add(this.tribute.current.collection.selectClass);
410 var liClientRect = li.getBoundingClientRect();
411 var menuClientRect = this.tribute.menu.getBoundingClientRect();
412
413 if (liClientRect.bottom > menuClientRect.bottom) {
414 var scrollDistance = liClientRect.bottom - menuClientRect.bottom;
415 this.tribute.menu.scrollTop += scrollDistance;
416 } else if (liClientRect.top < menuClientRect.top) {
417 var _scrollDistance = menuClientRect.top - liClientRect.top;
418
419 this.tribute.menu.scrollTop -= _scrollDistance;
420 }
421 } else {
422 li.classList.remove(this.tribute.current.collection.selectClass);
423 }
424 }
425 }
426 }, {
427 key: "getFullHeight",
428 value: function getFullHeight(elem, includeMargin) {
429 var height = elem.getBoundingClientRect().height;
430
431 if (includeMargin) {
432 var style = elem.currentStyle || window.getComputedStyle(elem);
433 return height + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
434 }
435
436 return height;
437 }
438 }], [{
439 key: "keys",
440 value: function keys() {
441 return [{
442 key: 9,
443 value: "TAB"
444 }, {
445 key: 8,
446 value: "DELETE"
447 }, {
448 key: 13,
449 value: "ENTER"
450 }, {
451 key: 27,
452 value: "ESCAPE"
453 }, {
454 key: 32,
455 value: "SPACE"
456 }, {
457 key: 38,
458 value: "UP"
459 }, {
460 key: 40,
461 value: "DOWN"
462 }];
463 }
464 }]);
465
466 return TributeEvents;
467 }();
468
469 var TributeMenuEvents = /*#__PURE__*/function () {
470 function TributeMenuEvents(tribute) {
471 _classCallCheck(this, TributeMenuEvents);
472
473 this.tribute = tribute;
474 this.tribute.menuEvents = this;
475 this.menu = this.tribute.menu;
476 }
477
478 _createClass(TributeMenuEvents, [{
479 key: "bind",
480 value: function bind(menu) {
481 var _this = this;
482
483 this.menuClickEvent = this.tribute.events.click.bind(null, this);
484 this.menuContainerScrollEvent = this.debounce(function () {
485 if (_this.tribute.isActive) {
486 _this.tribute.hideMenu();
487 }
488 }, 10, false);
489 this.windowResizeEvent = this.debounce(function () {
490 if (_this.tribute.isActive) {
491 _this.tribute.hideMenu();
492 }
493 }, 10, false); // fixes IE11 issues with mousedown
494
495 this.tribute.range.getDocument().addEventListener("MSPointerDown", this.menuClickEvent, false);
496 this.tribute.range.getDocument().addEventListener("mousedown", this.menuClickEvent, false);
497 window.addEventListener("resize", this.windowResizeEvent);
498
499 if (this.menuContainer) {
500 this.menuContainer.addEventListener("scroll", this.menuContainerScrollEvent, false);
501 } else {
502 window.addEventListener("scroll", this.menuContainerScrollEvent);
503 }
504 }
505 }, {
506 key: "unbind",
507 value: function unbind(menu) {
508 this.tribute.range.getDocument().removeEventListener("mousedown", this.menuClickEvent, false);
509 this.tribute.range.getDocument().removeEventListener("MSPointerDown", this.menuClickEvent, false);
510 window.removeEventListener("resize", this.windowResizeEvent);
511
512 if (this.menuContainer) {
513 this.menuContainer.removeEventListener("scroll", this.menuContainerScrollEvent, false);
514 } else {
515 window.removeEventListener("scroll", this.menuContainerScrollEvent);
516 }
517 }
518 }, {
519 key: "debounce",
520 value: function debounce(func, wait, immediate) {
521 var _arguments = arguments,
522 _this2 = this;
523
524 var timeout;
525 return function () {
526 var context = _this2,
527 args = _arguments;
528
529 var later = function later() {
530 timeout = null;
531 if (!immediate) func.apply(context, args);
532 };
533
534 var callNow = immediate && !timeout;
535 clearTimeout(timeout);
536 timeout = setTimeout(later, wait);
537 if (callNow) func.apply(context, args);
538 };
539 }
540 }]);
541
542 return TributeMenuEvents;
543 }();
544
545 var TributeRange = /*#__PURE__*/function () {
546 function TributeRange(tribute) {
547 _classCallCheck(this, TributeRange);
548
549 this.tribute = tribute;
550 this.tribute.range = this;
551 }
552
553 _createClass(TributeRange, [{
554 key: "getDocument",
555 value: function getDocument() {
556 var iframe;
557
558 if (this.tribute.current.collection) {
559 iframe = this.tribute.current.collection.iframe;
560 }
561
562 if (!iframe) {
563 return document;
564 }
565
566 return iframe.contentWindow.document;
567 }
568 }, {
569 key: "positionMenuAtCaret",
570 value: function positionMenuAtCaret(scrollTo) {
571 var context = this.tribute.current,
572 coordinates;
573 var info = this.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces, this.tribute.autocompleteMode);
574
575 if (typeof info !== 'undefined') {
576 if (!this.tribute.positionMenu) {
577 this.tribute.menu.style.cssText = "display: block;";
578 return;
579 }
580
581 if (!this.isContentEditable(context.element)) {
582 coordinates = this.getTextAreaOrInputUnderlinePosition(this.tribute.current.element, info.mentionPosition);
583 } else {
584 coordinates = this.getContentEditableCaretPosition(info.mentionPosition);
585 }
586
587 this.tribute.menu.style.cssText = "top: ".concat(coordinates.top, "px;\n left: ").concat(coordinates.left, "px;\n right: ").concat(coordinates.right, "px;\n bottom: ").concat(coordinates.bottom, "px;\n max-height: ").concat(coordinates.maxHeight || 500, "px;\n max-width: ").concat(coordinates.maxWidth || 300, "px;\n position: ").concat(coordinates.position || 'absolute', ";\n display: block;");
588
589 if (coordinates.left === 'auto') {
590 this.tribute.menu.style.left = 'auto';
591 }
592
593 if (coordinates.top === 'auto') {
594 this.tribute.menu.style.top = 'auto';
595 }
596
597 if (scrollTo) this.scrollIntoView();
598 } else {
599 this.tribute.menu.style.cssText = 'display: none';
600 }
601 }
602 }, {
603 key: "selectElement",
604 value: function selectElement(targetElement, path, offset) {
605 var range;
606 var elem = targetElement;
607
608 if (path) {
609 for (var i = 0; i < path.length; i++) {
610 elem = elem.childNodes[path[i]];
611
612 if (elem === undefined) {
613 return;
614 }
615
616 while (elem.length < offset) {
617 offset -= elem.length;
618 elem = elem.nextSibling;
619 }
620
621 if (elem.childNodes.length === 0 && !elem.length) {
622 elem = elem.previousSibling;
623 }
624 }
625 }
626
627 var sel = this.getWindowSelection();
628 range = this.getDocument().createRange();
629 range.setStart(elem, offset);
630 range.setEnd(elem, offset);
631 range.collapse(true);
632
633 try {
634 sel.removeAllRanges();
635 } catch (error) {}
636
637 sel.addRange(range);
638 targetElement.focus();
639 }
640 }, {
641 key: "replaceTriggerText",
642 value: function replaceTriggerText(text, requireLeadingSpace, hasTrailingSpace, originalEvent, item) {
643 var info = this.getTriggerInfo(true, hasTrailingSpace, requireLeadingSpace, this.tribute.allowSpaces, this.tribute.autocompleteMode);
644
645 if (info !== undefined) {
646 var context = this.tribute.current;
647 var replaceEvent = new CustomEvent('tribute-replaced', {
648 detail: {
649 item: item,
650 instance: context,
651 context: info,
652 event: originalEvent
653 }
654 });
655
656 if (!this.isContentEditable(context.element)) {
657 var myField = this.tribute.current.element;
658 var textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : ' ';
659 text += textSuffix;
660 var startPos = info.mentionPosition;
661 var endPos = info.mentionPosition + info.mentionText.length + textSuffix.length;
662
663 if (!this.tribute.autocompleteMode) {
664 endPos += info.mentionTriggerChar.length - 1;
665 }
666
667 myField.value = myField.value.substring(0, startPos) + text + myField.value.substring(endPos, myField.value.length);
668 myField.selectionStart = startPos + text.length;
669 myField.selectionEnd = startPos + text.length;
670 } else {
671 // add a space to the end of the pasted text
672 var _textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : '\xA0';
673
674 text += _textSuffix;
675
676 var _endPos = info.mentionPosition + info.mentionText.length;
677
678 if (!this.tribute.autocompleteMode) {
679 _endPos += info.mentionTriggerChar.length;
680 }
681
682 this.pasteHtml(text, info.mentionPosition, _endPos);
683 }
684
685 context.element.dispatchEvent(new CustomEvent('input', {
686 bubbles: true
687 }));
688 context.element.dispatchEvent(replaceEvent);
689 }
690 }
691 }, {
692 key: "pasteHtml",
693 value: function pasteHtml(html, startPos, endPos) {
694 var range, sel;
695 sel = this.getWindowSelection();
696 range = this.getDocument().createRange();
697 range.setStart(sel.anchorNode, startPos);
698 range.setEnd(sel.anchorNode, endPos);
699 range.deleteContents();
700 var el = this.getDocument().createElement('div');
701 el.innerHTML = html;
702 var frag = this.getDocument().createDocumentFragment(),
703 node,
704 lastNode;
705
706 while (node = el.firstChild) {
707 lastNode = frag.appendChild(node);
708 }
709
710 range.insertNode(frag); // Preserve the selection
711
712 if (lastNode) {
713 range = range.cloneRange();
714 range.setStartAfter(lastNode);
715 range.collapse(true);
716 sel.removeAllRanges();
717 sel.addRange(range);
718 }
719 }
720 }, {
721 key: "getWindowSelection",
722 value: function getWindowSelection() {
723 if (this.tribute.collection.iframe) {
724 return this.tribute.collection.iframe.contentWindow.getSelection();
725 }
726
727 return window.getSelection();
728 }
729 }, {
730 key: "getNodePositionInParent",
731 value: function getNodePositionInParent(element) {
732 if (element.parentNode === null) {
733 return 0;
734 }
735
736 for (var i = 0; i < element.parentNode.childNodes.length; i++) {
737 var node = element.parentNode.childNodes[i];
738
739 if (node === element) {
740 return i;
741 }
742 }
743 }
744 }, {
745 key: "getContentEditableSelectedPath",
746 value: function getContentEditableSelectedPath(ctx) {
747 var sel = this.getWindowSelection();
748 var selected = sel.anchorNode;
749 var path = [];
750 var offset;
751
752 if (selected != null) {
753 var i;
754 var ce = selected.contentEditable;
755
756 while (selected !== null && ce !== 'true') {
757 i = this.getNodePositionInParent(selected);
758 path.push(i);
759 selected = selected.parentNode;
760
761 if (selected !== null) {
762 ce = selected.contentEditable;
763 }
764 }
765
766 path.reverse(); // getRangeAt may not exist, need alternative
767
768 offset = sel.getRangeAt(0).startOffset;
769 return {
770 selected: selected,
771 path: path,
772 offset: offset
773 };
774 }
775 }
776 }, {
777 key: "getTextPrecedingCurrentSelection",
778 value: function getTextPrecedingCurrentSelection() {
779 var context = this.tribute.current,
780 text = '';
781
782 if (!this.isContentEditable(context.element)) {
783 var textComponent = this.tribute.current.element;
784
785 if (textComponent) {
786 var startPos = textComponent.selectionStart;
787
788 if (textComponent.value && startPos >= 0) {
789 text = textComponent.value.substring(0, startPos);
790 }
791 }
792 } else {
793 var selectedElem = this.getWindowSelection().anchorNode;
794
795 if (selectedElem != null) {
796 var workingNodeContent = selectedElem.textContent;
797 var selectStartOffset = this.getWindowSelection().getRangeAt(0).startOffset;
798
799 if (workingNodeContent && selectStartOffset >= 0) {
800 text = workingNodeContent.substring(0, selectStartOffset);
801 }
802 }
803 }
804
805 return text;
806 }
807 }, {
808 key: "getLastWordInText",
809 value: function getLastWordInText(text) {
810 text = text.replace(/\u00A0/g, ' '); // https://stackoverflow.com/questions/29850407/how-do-i-replace-unicode-character-u00a0-with-a-space-in-javascript
811
812 var wordsArray;
813
814 if (this.tribute.autocompleteSeparator) {
815 wordsArray = text.split(this.tribute.autocompleteSeparator);
816 } else {
817 wordsArray = text.split(/\s+/);
818 }
819
820 var worldsCount = wordsArray.length - 1;
821 return wordsArray[worldsCount].trim();
822 }
823 }, {
824 key: "getTriggerInfo",
825 value: function getTriggerInfo(menuAlreadyActive, hasTrailingSpace, requireLeadingSpace, allowSpaces, isAutocomplete) {
826 var _this = this;
827
828 var ctx = this.tribute.current;
829 var selected, path, offset;
830
831 if (!this.isContentEditable(ctx.element)) {
832 selected = this.tribute.current.element;
833 } else {
834 var selectionInfo = this.getContentEditableSelectedPath(ctx);
835
836 if (selectionInfo) {
837 selected = selectionInfo.selected;
838 path = selectionInfo.path;
839 offset = selectionInfo.offset;
840 }
841 }
842
843 var effectiveRange = this.getTextPrecedingCurrentSelection();
844 var lastWordOfEffectiveRange = this.getLastWordInText(effectiveRange);
845
846 if (isAutocomplete) {
847 return {
848 mentionPosition: effectiveRange.length - lastWordOfEffectiveRange.length,
849 mentionText: lastWordOfEffectiveRange,
850 mentionSelectedElement: selected,
851 mentionSelectedPath: path,
852 mentionSelectedOffset: offset
853 };
854 }
855
856 if (effectiveRange !== undefined && effectiveRange !== null) {
857 var mostRecentTriggerCharPos = -1;
858 var triggerChar;
859 this.tribute.collection.forEach(function (config) {
860 var c = config.trigger;
861 var idx = config.requireLeadingSpace ? _this.lastIndexWithLeadingSpace(effectiveRange, c) : effectiveRange.lastIndexOf(c);
862
863 if (idx > mostRecentTriggerCharPos) {
864 mostRecentTriggerCharPos = idx;
865 triggerChar = c;
866 requireLeadingSpace = config.requireLeadingSpace;
867 }
868 });
869
870 if (mostRecentTriggerCharPos >= 0 && (mostRecentTriggerCharPos === 0 || !requireLeadingSpace || /[\xA0\s]/g.test(effectiveRange.substring(mostRecentTriggerCharPos - 1, mostRecentTriggerCharPos)))) {
871 var currentTriggerSnippet = effectiveRange.substring(mostRecentTriggerCharPos + triggerChar.length, effectiveRange.length);
872 triggerChar = effectiveRange.substring(mostRecentTriggerCharPos, mostRecentTriggerCharPos + triggerChar.length);
873 var firstSnippetChar = currentTriggerSnippet.substring(0, 1);
874 var leadingSpace = currentTriggerSnippet.length > 0 && (firstSnippetChar === ' ' || firstSnippetChar === '\xA0');
875
876 if (hasTrailingSpace) {
877 currentTriggerSnippet = currentTriggerSnippet.trim();
878 }
879
880 var regex = allowSpaces ? /[^\S ]/g : /[\xA0\s]/g;
881 this.tribute.hasTrailingSpace = regex.test(currentTriggerSnippet);
882
883 if (!leadingSpace && (menuAlreadyActive || !regex.test(currentTriggerSnippet))) {
884 return {
885 mentionPosition: mostRecentTriggerCharPos,
886 mentionText: currentTriggerSnippet,
887 mentionSelectedElement: selected,
888 mentionSelectedPath: path,
889 mentionSelectedOffset: offset,
890 mentionTriggerChar: triggerChar
891 };
892 }
893 }
894 }
895 }
896 }, {
897 key: "lastIndexWithLeadingSpace",
898 value: function lastIndexWithLeadingSpace(str, trigger) {
899 var reversedStr = str.split('').reverse().join('');
900 var index = -1;
901
902 for (var cidx = 0, len = str.length; cidx < len; cidx++) {
903 var firstChar = cidx === str.length - 1;
904 var leadingSpace = /\s/.test(reversedStr[cidx + 1]);
905 var match = true;
906
907 for (var triggerIdx = trigger.length - 1; triggerIdx >= 0; triggerIdx--) {
908 if (trigger[triggerIdx] !== reversedStr[cidx - triggerIdx]) {
909 match = false;
910 break;
911 }
912 }
913
914 if (match && (firstChar || leadingSpace)) {
915 index = str.length - 1 - cidx;
916 break;
917 }
918 }
919
920 return index;
921 }
922 }, {
923 key: "isContentEditable",
924 value: function isContentEditable(element) {
925 return element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA';
926 }
927 }, {
928 key: "isMenuOffScreen",
929 value: function isMenuOffScreen(coordinates, menuDimensions) {
930 var windowWidth = window.innerWidth;
931 var windowHeight = window.innerHeight;
932 var doc = document.documentElement;
933 var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
934 var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
935 var menuTop = typeof coordinates.top === 'number' ? coordinates.top : windowTop + windowHeight - coordinates.bottom - menuDimensions.height;
936 var menuRight = typeof coordinates.right === 'number' ? coordinates.right : coordinates.left + menuDimensions.width;
937 var menuBottom = typeof coordinates.bottom === 'number' ? coordinates.bottom : coordinates.top + menuDimensions.height;
938 var menuLeft = typeof coordinates.left === 'number' ? coordinates.left : windowLeft + windowWidth - coordinates.right - menuDimensions.width;
939 return {
940 top: menuTop < Math.floor(windowTop),
941 right: menuRight > Math.ceil(windowLeft + windowWidth),
942 bottom: menuBottom > Math.ceil(windowTop + windowHeight),
943 left: menuLeft < Math.floor(windowLeft)
944 };
945 }
946 }, {
947 key: "getMenuDimensions",
948 value: function getMenuDimensions() {
949 // Width of the menu depends of its contents and position
950 // We must check what its width would be without any obstruction
951 // This way, we can achieve good positioning for flipping the menu
952 var dimensions = {
953 width: null,
954 height: null
955 };
956 this.tribute.menu.style.cssText = "top: 0px;\n left: 0px;\n position: fixed;\n display: block;\n visibility; hidden;\n max-height:500px;";
957 dimensions.width = this.tribute.menu.offsetWidth;
958 dimensions.height = this.tribute.menu.offsetHeight;
959 this.tribute.menu.style.cssText = "display: none;";
960 return dimensions;
961 }
962 }, {
963 key: "getTextAreaOrInputUnderlinePosition",
964 value: function getTextAreaOrInputUnderlinePosition(element, position, flipped) {
965 var properties = ['direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'borderStyle', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing'];
966 var div = this.getDocument().createElement('div');
967 div.id = 'input-textarea-caret-position-mirror-div';
968 this.getDocument().body.appendChild(div);
969 var style = div.style;
970 var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle;
971 style.whiteSpace = 'pre-wrap';
972
973 if (element.nodeName !== 'INPUT') {
974 style.wordWrap = 'break-word';
975 }
976
977 style.position = 'absolute';
978 style.visibility = 'hidden'; // transfer the element's properties to the div
979
980 properties.forEach(function (prop) {
981 style[prop] = computed[prop];
982 }); //NOT SURE WHY THIS IS HERE AND IT DOESNT SEEM HELPFUL
983 // if (isFirefox) {
984 // style.width = `${(parseInt(computed.width) - 2)}px`
985 // if (element.scrollHeight > parseInt(computed.height))
986 // style.overflowY = 'scroll'
987 // } else {
988 // style.overflow = 'hidden'
989 // }
990
991 var span0 = document.createElement('span');
992 span0.textContent = element.value.substring(0, position);
993 div.appendChild(span0);
994
995 if (element.nodeName === 'INPUT') {
996 div.textContent = div.textContent.replace(/\s/g, ' ');
997 } //Create a span in the div that represents where the cursor
998 //should be
999
1000
1001 var span = this.getDocument().createElement('span'); //we give it no content as this represents the cursor
1002
1003 span.textContent = '&#x200B;';
1004 div.appendChild(span);
1005 var span2 = this.getDocument().createElement('span');
1006 span2.textContent = element.value.substring(position);
1007 div.appendChild(span2);
1008 var rect = element.getBoundingClientRect(); //position the div exactly over the element
1009 //so we can get the bounding client rect for the span and
1010 //it should represent exactly where the cursor is
1011
1012 div.style.position = 'fixed';
1013 div.style.left = rect.left + 'px';
1014 div.style.top = rect.top + 'px';
1015 div.style.width = rect.width + 'px';
1016 div.style.height = rect.height + 'px';
1017 div.scrollTop = element.scrollTop;
1018 var spanRect = span.getBoundingClientRect();
1019 this.getDocument().body.removeChild(div);
1020 return this.getFixedCoordinatesRelativeToRect(spanRect);
1021 }
1022 }, {
1023 key: "getContentEditableCaretPosition",
1024 value: function getContentEditableCaretPosition(selectedNodePosition) {
1025 var range;
1026 var sel = this.getWindowSelection();
1027 range = this.getDocument().createRange();
1028 range.setStart(sel.anchorNode, selectedNodePosition);
1029 range.setEnd(sel.anchorNode, selectedNodePosition);
1030 range.collapse(false);
1031 var rect = range.getBoundingClientRect();
1032 return this.getFixedCoordinatesRelativeToRect(rect);
1033 }
1034 }, {
1035 key: "getFixedCoordinatesRelativeToRect",
1036 value: function getFixedCoordinatesRelativeToRect(rect) {
1037 var coordinates = {
1038 position: 'fixed',
1039 left: rect.left,
1040 top: rect.top + rect.height
1041 };
1042 var menuDimensions = this.getMenuDimensions();
1043 var availableSpaceOnTop = rect.top;
1044 var availableSpaceOnBottom = window.innerHeight - (rect.top + rect.height); //check to see where's the right place to put the menu vertically
1045
1046 if (availableSpaceOnBottom < menuDimensions.height) {
1047 if (availableSpaceOnTop >= menuDimensions.height || availableSpaceOnTop > availableSpaceOnBottom) {
1048 coordinates.top = 'auto';
1049 coordinates.bottom = window.innerHeight - rect.top;
1050
1051 if (availableSpaceOnBottom < menuDimensions.height) {
1052 coordinates.maxHeight = availableSpaceOnTop;
1053 }
1054 } else {
1055 if (availableSpaceOnTop < menuDimensions.height) {
1056 coordinates.maxHeight = availableSpaceOnBottom;
1057 }
1058 }
1059 }
1060
1061 var availableSpaceOnLeft = rect.left;
1062 var availableSpaceOnRight = window.innerWidth - rect.left; //check to see where's the right place to put the menu horizontally
1063
1064 if (availableSpaceOnRight < menuDimensions.width) {
1065 if (availableSpaceOnLeft >= menuDimensions.width || availableSpaceOnLeft > availableSpaceOnRight) {
1066 coordinates.left = 'auto';
1067 coordinates.right = window.innerWidth - rect.left;
1068
1069 if (availableSpaceOnRight < menuDimensions.width) {
1070 coordinates.maxWidth = availableSpaceOnLeft;
1071 }
1072 } else {
1073 if (availableSpaceOnLeft < menuDimensions.width) {
1074 coordinates.maxWidth = availableSpaceOnRight;
1075 }
1076 }
1077 }
1078
1079 return coordinates;
1080 }
1081 }, {
1082 key: "scrollIntoView",
1083 value: function scrollIntoView(elem) {
1084 var reasonableBuffer = 20,
1085 clientRect;
1086 var maxScrollDisplacement = 100;
1087 var e = this.menu;
1088 if (typeof e === 'undefined') return;
1089
1090 while (clientRect === undefined || clientRect.height === 0) {
1091 clientRect = e.getBoundingClientRect();
1092
1093 if (clientRect.height === 0) {
1094 e = e.childNodes[0];
1095
1096 if (e === undefined || !e.getBoundingClientRect) {
1097 return;
1098 }
1099 }
1100 }
1101
1102 var elemTop = clientRect.top;
1103 var elemBottom = elemTop + clientRect.height;
1104
1105 if (elemTop < 0) {
1106 window.scrollTo(0, window.pageYOffset + clientRect.top - reasonableBuffer);
1107 } else if (elemBottom > window.innerHeight) {
1108 var maxY = window.pageYOffset + clientRect.top - reasonableBuffer;
1109
1110 if (maxY - window.pageYOffset > maxScrollDisplacement) {
1111 maxY = window.pageYOffset + maxScrollDisplacement;
1112 }
1113
1114 var targetY = window.pageYOffset - (window.innerHeight - elemBottom);
1115
1116 if (targetY > maxY) {
1117 targetY = maxY;
1118 }
1119
1120 window.scrollTo(0, targetY);
1121 }
1122 }
1123 }, {
1124 key: "menuContainerIsBody",
1125 get: function get() {
1126 return this.tribute.menuContainer === document.body || !this.tribute.menuContainer;
1127 }
1128 }]);
1129
1130 return TributeRange;
1131 }();
1132
1133 // Thanks to https://github.com/mattyork/fuzzy
1134 var TributeSearch = /*#__PURE__*/function () {
1135 function TributeSearch(tribute) {
1136 _classCallCheck(this, TributeSearch);
1137
1138 this.tribute = tribute;
1139 this.tribute.search = this;
1140 }
1141
1142 _createClass(TributeSearch, [{
1143 key: "simpleFilter",
1144 value: function simpleFilter(pattern, array) {
1145 var _this = this;
1146
1147 return array.filter(function (string) {
1148 return _this.test(pattern, string);
1149 });
1150 }
1151 }, {
1152 key: "test",
1153 value: function test(pattern, string) {
1154 return this.match(pattern, string) !== null;
1155 }
1156 }, {
1157 key: "match",
1158 value: function match(pattern, string, opts) {
1159 opts = opts || {};
1160 var len = string.length,
1161 pre = opts.pre || '',
1162 post = opts.post || '',
1163 compareString = opts.caseSensitive && string || string.toLowerCase();
1164
1165 if (opts.skip) {
1166 return {
1167 rendered: string,
1168 score: 0
1169 };
1170 }
1171
1172 pattern = opts.caseSensitive && pattern || pattern.toLowerCase();
1173 var patternCache = this.traverse(compareString, pattern, 0, 0, []);
1174
1175 if (!patternCache) {
1176 return null;
1177 }
1178
1179 return {
1180 rendered: this.render(string, patternCache.cache, pre, post),
1181 score: patternCache.score
1182 };
1183 }
1184 }, {
1185 key: "traverse",
1186 value: function traverse(string, pattern, stringIndex, patternIndex, patternCache) {
1187 if (this.tribute.autocompleteSeparator) {
1188 // if the pattern search at end
1189 pattern = pattern.split(this.tribute.autocompleteSeparator).splice(-1)[0];
1190 }
1191
1192 if (pattern.length === patternIndex) {
1193 // calculate score and copy the cache containing the indices where it's found
1194 return {
1195 score: this.calculateScore(patternCache),
1196 cache: patternCache.slice()
1197 };
1198 } // if string at end or remaining pattern > remaining string
1199
1200
1201 if (string.length === stringIndex || pattern.length - patternIndex > string.length - stringIndex) {
1202 return undefined;
1203 }
1204
1205 var c = pattern[patternIndex];
1206 var index = string.indexOf(c, stringIndex);
1207 var best, temp;
1208
1209 while (index > -1) {
1210 patternCache.push(index);
1211 temp = this.traverse(string, pattern, index + 1, patternIndex + 1, patternCache);
1212 patternCache.pop(); // if downstream traversal failed, return best answer so far
1213
1214 if (!temp) {
1215 return best;
1216 }
1217
1218 if (!best || best.score < temp.score) {
1219 best = temp;
1220 }
1221
1222 index = string.indexOf(c, index + 1);
1223 }
1224
1225 return best;
1226 }
1227 }, {
1228 key: "calculateScore",
1229 value: function calculateScore(patternCache) {
1230 var score = 0;
1231 var temp = 1;
1232 patternCache.forEach(function (index, i) {
1233 if (i > 0) {
1234 if (patternCache[i - 1] + 1 === index) {
1235 temp += temp + 1;
1236 } else {
1237 temp = 1;
1238 }
1239 }
1240
1241 score += temp;
1242 });
1243 return score;
1244 }
1245 }, {
1246 key: "render",
1247 value: function render(string, indices, pre, post) {
1248 var rendered = string.substring(0, indices[0]);
1249 indices.forEach(function (index, i) {
1250 rendered += pre + string[index] + post + string.substring(index + 1, indices[i + 1] ? indices[i + 1] : string.length);
1251 });
1252 return rendered;
1253 }
1254 }, {
1255 key: "filter",
1256 value: function filter(pattern, arr, opts) {
1257 var _this2 = this;
1258
1259 opts = opts || {};
1260 return arr.reduce(function (prev, element, idx, arr) {
1261 var str = element;
1262
1263 if (opts.extract) {
1264 str = opts.extract(element);
1265
1266 if (!str) {
1267 // take care of undefineds / nulls / etc.
1268 str = '';
1269 }
1270 }
1271
1272 var rendered = _this2.match(pattern, str, opts);
1273
1274 if (rendered != null) {
1275 prev[prev.length] = {
1276 string: rendered.rendered,
1277 score: rendered.score,
1278 index: idx,
1279 original: element
1280 };
1281 }
1282
1283 return prev;
1284 }, []).sort(function (a, b) {
1285 var compare = b.score - a.score;
1286 if (compare) return compare;
1287 return a.index - b.index;
1288 });
1289 }
1290 }]);
1291
1292 return TributeSearch;
1293 }();
1294
1295 var Tribute = /*#__PURE__*/function () {
1296 function Tribute(_ref) {
1297 var _this = this;
1298
1299 var _ref$values = _ref.values,
1300 values = _ref$values === void 0 ? null : _ref$values,
1301 _ref$loadingItemTempl = _ref.loadingItemTemplate,
1302 loadingItemTemplate = _ref$loadingItemTempl === void 0 ? null : _ref$loadingItemTempl,
1303 _ref$iframe = _ref.iframe,
1304 iframe = _ref$iframe === void 0 ? null : _ref$iframe,
1305 _ref$selectClass = _ref.selectClass,
1306 selectClass = _ref$selectClass === void 0 ? "highlight" : _ref$selectClass,
1307 _ref$containerClass = _ref.containerClass,
1308 containerClass = _ref$containerClass === void 0 ? "tribute-container" : _ref$containerClass,
1309 _ref$itemClass = _ref.itemClass,
1310 itemClass = _ref$itemClass === void 0 ? "" : _ref$itemClass,
1311 _ref$trigger = _ref.trigger,
1312 trigger = _ref$trigger === void 0 ? "@" : _ref$trigger,
1313 _ref$autocompleteMode = _ref.autocompleteMode,
1314 autocompleteMode = _ref$autocompleteMode === void 0 ? false : _ref$autocompleteMode,
1315 _ref$autocompleteSepa = _ref.autocompleteSeparator,
1316 autocompleteSeparator = _ref$autocompleteSepa === void 0 ? null : _ref$autocompleteSepa,
1317 _ref$selectTemplate = _ref.selectTemplate,
1318 selectTemplate = _ref$selectTemplate === void 0 ? null : _ref$selectTemplate,
1319 _ref$menuItemTemplate = _ref.menuItemTemplate,
1320 menuItemTemplate = _ref$menuItemTemplate === void 0 ? null : _ref$menuItemTemplate,
1321 _ref$lookup = _ref.lookup,
1322 lookup = _ref$lookup === void 0 ? "key" : _ref$lookup,
1323 _ref$fillAttr = _ref.fillAttr,
1324 fillAttr = _ref$fillAttr === void 0 ? "value" : _ref$fillAttr,
1325 _ref$collection = _ref.collection,
1326 collection = _ref$collection === void 0 ? null : _ref$collection,
1327 _ref$menuContainer = _ref.menuContainer,
1328 menuContainer = _ref$menuContainer === void 0 ? null : _ref$menuContainer,
1329 _ref$noMatchTemplate = _ref.noMatchTemplate,
1330 noMatchTemplate = _ref$noMatchTemplate === void 0 ? null : _ref$noMatchTemplate,
1331 _ref$requireLeadingSp = _ref.requireLeadingSpace,
1332 requireLeadingSpace = _ref$requireLeadingSp === void 0 ? true : _ref$requireLeadingSp,
1333 _ref$allowSpaces = _ref.allowSpaces,
1334 allowSpaces = _ref$allowSpaces === void 0 ? false : _ref$allowSpaces,
1335 _ref$replaceTextSuffi = _ref.replaceTextSuffix,
1336 replaceTextSuffix = _ref$replaceTextSuffi === void 0 ? null : _ref$replaceTextSuffi,
1337 _ref$positionMenu = _ref.positionMenu,
1338 positionMenu = _ref$positionMenu === void 0 ? true : _ref$positionMenu,
1339 _ref$spaceSelectsMatc = _ref.spaceSelectsMatch,
1340 spaceSelectsMatch = _ref$spaceSelectsMatc === void 0 ? false : _ref$spaceSelectsMatc,
1341 _ref$searchOpts = _ref.searchOpts,
1342 searchOpts = _ref$searchOpts === void 0 ? {} : _ref$searchOpts,
1343 _ref$menuItemLimit = _ref.menuItemLimit,
1344 menuItemLimit = _ref$menuItemLimit === void 0 ? null : _ref$menuItemLimit,
1345 _ref$menuShowMinLengt = _ref.menuShowMinLength,
1346 menuShowMinLength = _ref$menuShowMinLengt === void 0 ? 0 : _ref$menuShowMinLengt;
1347
1348 _classCallCheck(this, Tribute);
1349
1350 this.autocompleteMode = autocompleteMode;
1351 this.autocompleteSeparator = autocompleteSeparator;
1352 this.menuSelected = 0;
1353 this.current = {};
1354 this.inputEvent = false;
1355 this.isActive = false;
1356 this.menuContainer = menuContainer;
1357 this.allowSpaces = allowSpaces;
1358 this.replaceTextSuffix = replaceTextSuffix;
1359 this.positionMenu = positionMenu;
1360 this.hasTrailingSpace = false;
1361 this.spaceSelectsMatch = spaceSelectsMatch;
1362
1363 if (this.autocompleteMode) {
1364 trigger = "";
1365 allowSpaces = false;
1366 }
1367
1368 if (values) {
1369 this.collection = [{
1370 // symbol that starts the lookup
1371 trigger: trigger,
1372 // is it wrapped in an iframe
1373 iframe: iframe,
1374 // class applied to selected item
1375 selectClass: selectClass,
1376 // class applied to the Container
1377 containerClass: containerClass,
1378 // class applied to each item
1379 itemClass: itemClass,
1380 // function called on select that retuns the content to insert
1381 selectTemplate: (selectTemplate || Tribute.defaultSelectTemplate).bind(this),
1382 // function called that returns content for an item
1383 menuItemTemplate: (menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(this),
1384 // function called when menu is empty, disables hiding of menu.
1385 noMatchTemplate: function (t) {
1386 if (typeof t === "string") {
1387 if (t.trim() === "") return null;
1388 return t;
1389 }
1390
1391 if (typeof t === "function") {
1392 return t.bind(_this);
1393 }
1394
1395 return noMatchTemplate || function () {
1396 return "<li>No Match Found!</li>";
1397 }.bind(_this);
1398 }(noMatchTemplate),
1399 // column to search against in the object
1400 lookup: lookup,
1401 // column that contains the content to insert by default
1402 fillAttr: fillAttr,
1403 // array of objects or a function returning an array of objects
1404 values: values,
1405 // useful for when values is an async function
1406 loadingItemTemplate: loadingItemTemplate,
1407 requireLeadingSpace: requireLeadingSpace,
1408 searchOpts: searchOpts,
1409 menuItemLimit: menuItemLimit,
1410 menuShowMinLength: menuShowMinLength
1411 }];
1412 } else if (collection) {
1413 if (this.autocompleteMode) console.warn("Tribute in autocomplete mode does not work for collections");
1414 this.collection = collection.map(function (item) {
1415 return {
1416 trigger: item.trigger || trigger,
1417 iframe: item.iframe || iframe,
1418 selectClass: item.selectClass || selectClass,
1419 containerClass: item.containerClass || containerClass,
1420 itemClass: item.itemClass || itemClass,
1421 selectTemplate: (item.selectTemplate || Tribute.defaultSelectTemplate).bind(_this),
1422 menuItemTemplate: (item.menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(_this),
1423 // function called when menu is empty, disables hiding of menu.
1424 noMatchTemplate: function (t) {
1425 if (typeof t === "string") {
1426 if (t.trim() === "") return null;
1427 return t;
1428 }
1429
1430 if (typeof t === "function") {
1431 return t.bind(_this);
1432 }
1433
1434 return noMatchTemplate || function () {
1435 return "<li>No Match Found!</li>";
1436 }.bind(_this);
1437 }(noMatchTemplate),
1438 lookup: item.lookup || lookup,
1439 fillAttr: item.fillAttr || fillAttr,
1440 values: item.values,
1441 loadingItemTemplate: item.loadingItemTemplate,
1442 requireLeadingSpace: item.requireLeadingSpace,
1443 searchOpts: item.searchOpts || searchOpts,
1444 menuItemLimit: item.menuItemLimit || menuItemLimit,
1445 menuShowMinLength: item.menuShowMinLength || menuShowMinLength
1446 };
1447 });
1448 } else {
1449 throw new Error("[Tribute] No collection specified.");
1450 }
1451
1452 new TributeRange(this);
1453 new TributeEvents(this);
1454 new TributeMenuEvents(this);
1455 new TributeSearch(this);
1456 }
1457
1458 _createClass(Tribute, [{
1459 key: "triggers",
1460 value: function triggers() {
1461 return this.collection.map(function (config) {
1462 return config.trigger;
1463 });
1464 }
1465 }, {
1466 key: "attach",
1467 value: function attach(el) {
1468 if (!el) {
1469 throw new Error("[Tribute] Must pass in a DOM node or NodeList.");
1470 } // Check if it is a jQuery collection
1471
1472
1473 if (typeof jQuery !== "undefined" && el instanceof jQuery) {
1474 el = el.get();
1475 } // Is el an Array/Array-like object?
1476
1477
1478 if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
1479 var length = el.length;
1480
1481 for (var i = 0; i < length; ++i) {
1482 this._attach(el[i]);
1483 }
1484 } else {
1485 this._attach(el);
1486 }
1487 }
1488 }, {
1489 key: "_attach",
1490 value: function _attach(el) {
1491 if (el.hasAttribute("data-tribute")) {
1492 console.warn("Tribute was already bound to " + el.nodeName);
1493 }
1494
1495 this.ensureEditable(el);
1496 this.events.bind(el);
1497 el.setAttribute("data-tribute", true);
1498 }
1499 }, {
1500 key: "ensureEditable",
1501 value: function ensureEditable(element) {
1502 if (Tribute.inputTypes().indexOf(element.nodeName) === -1) {
1503 if (element.contentEditable) {
1504 element.contentEditable = true;
1505 } else {
1506 throw new Error("[Tribute] Cannot bind to " + element.nodeName);
1507 }
1508 }
1509 }
1510 }, {
1511 key: "createMenu",
1512 value: function createMenu(containerClass) {
1513 var wrapper = this.range.getDocument().createElement("div"),
1514 ul = this.range.getDocument().createElement("ul");
1515 wrapper.className = containerClass;
1516 wrapper.appendChild(ul);
1517
1518 if (this.menuContainer) {
1519 return this.menuContainer.appendChild(wrapper);
1520 }
1521
1522 return this.range.getDocument().body.appendChild(wrapper);
1523 }
1524 }, {
1525 key: "showMenuFor",
1526 value: function showMenuFor(element, scrollTo) {
1527 var _this2 = this;
1528
1529 // Only proceed if menu isn't already shown for the current element & mentionText
1530 if (this.isActive && this.current.element === element && this.current.mentionText === this.currentMentionTextSnapshot) {
1531 return;
1532 }
1533
1534 this.currentMentionTextSnapshot = this.current.mentionText; // create the menu if it doesn't exist.
1535
1536 if (!this.menu) {
1537 this.menu = this.createMenu(this.current.collection.containerClass);
1538 element.tributeMenu = this.menu;
1539 this.menuEvents.bind(this.menu);
1540 }
1541
1542 this.isActive = true;
1543 this.menuSelected = 0;
1544
1545 if (!this.current.mentionText) {
1546 this.current.mentionText = "";
1547 }
1548
1549 var processValues = function processValues(values) {
1550 // Tribute may not be active any more by the time the value callback returns
1551 if (!_this2.isActive) {
1552 return;
1553 }
1554
1555 var items = _this2.search.filter(_this2.current.mentionText, values, {
1556 pre: _this2.current.collection.searchOpts.pre || "<span>",
1557 post: _this2.current.collection.searchOpts.post || "</span>",
1558 skip: _this2.current.collection.searchOpts.skip,
1559 extract: function extract(el) {
1560 if (typeof _this2.current.collection.lookup === "string") {
1561 return el[_this2.current.collection.lookup];
1562 } else if (typeof _this2.current.collection.lookup === "function") {
1563 return _this2.current.collection.lookup(el, _this2.current.mentionText);
1564 } else {
1565 throw new Error("Invalid lookup attribute, lookup must be string or function.");
1566 }
1567 }
1568 });
1569
1570 if (_this2.current.collection.menuItemLimit) {
1571 items = items.slice(0, _this2.current.collection.menuItemLimit);
1572 }
1573
1574 _this2.current.filteredItems = items;
1575
1576 var ul = _this2.menu.querySelector("ul");
1577
1578 if (!items.length) {
1579 var noMatchEvent = new CustomEvent("tribute-no-match", {
1580 detail: _this2.menu
1581 });
1582
1583 _this2.current.element.dispatchEvent(noMatchEvent);
1584
1585 if (typeof _this2.current.collection.noMatchTemplate === "function" && !_this2.current.collection.noMatchTemplate() || !_this2.current.collection.noMatchTemplate) {
1586 _this2.hideMenu();
1587 } else {
1588 typeof _this2.current.collection.noMatchTemplate === "function" ? ul.innerHTML = _this2.current.collection.noMatchTemplate() : ul.innerHTML = _this2.current.collection.noMatchTemplate;
1589
1590 _this2.range.positionMenuAtCaret(scrollTo);
1591 }
1592
1593 return;
1594 }
1595
1596 ul.innerHTML = "";
1597
1598 var fragment = _this2.range.getDocument().createDocumentFragment();
1599
1600 items.forEach(function (item, index) {
1601 var li = _this2.range.getDocument().createElement("li");
1602
1603 li.setAttribute("data-index", index);
1604 li.className = _this2.current.collection.itemClass;
1605 li.addEventListener("mousemove", function (e) {
1606 var _this2$_findLiTarget = _this2._findLiTarget(e.target),
1607 _this2$_findLiTarget2 = _slicedToArray(_this2$_findLiTarget, 2),
1608 li = _this2$_findLiTarget2[0],
1609 index = _this2$_findLiTarget2[1];
1610
1611 if (e.movementY !== 0) {
1612 _this2.events.setActiveLi(index);
1613 }
1614 });
1615
1616 if (_this2.menuSelected === index) {
1617 li.classList.add(_this2.current.collection.selectClass);
1618 }
1619
1620 li.innerHTML = _this2.current.collection.menuItemTemplate(item);
1621 fragment.appendChild(li);
1622 });
1623 ul.appendChild(fragment);
1624
1625 _this2.range.positionMenuAtCaret(scrollTo);
1626 };
1627
1628 if (typeof this.current.collection.values === "function") {
1629 if (this.current.collection.loadingItemTemplate) {
1630 this.menu.querySelector("ul").innerHTML = this.current.collection.loadingItemTemplate;
1631 this.range.positionMenuAtCaret(scrollTo);
1632 }
1633
1634 this.current.collection.values(this.current.mentionText, processValues);
1635 } else {
1636 processValues(this.current.collection.values);
1637 }
1638 }
1639 }, {
1640 key: "_findLiTarget",
1641 value: function _findLiTarget(el) {
1642 if (!el) return [];
1643 var index = el.getAttribute("data-index");
1644 return !index ? this._findLiTarget(el.parentNode) : [el, index];
1645 }
1646 }, {
1647 key: "showMenuForCollection",
1648 value: function showMenuForCollection(element, collectionIndex) {
1649 if (element !== document.activeElement) {
1650 this.placeCaretAtEnd(element);
1651 }
1652
1653 this.current.collection = this.collection[collectionIndex || 0];
1654 this.current.externalTrigger = true;
1655 this.current.element = element;
1656 if (element.isContentEditable) this.insertTextAtCursor(this.current.collection.trigger);else this.insertAtCaret(element, this.current.collection.trigger);
1657 this.showMenuFor(element);
1658 } // TODO: make sure this works for inputs/textareas
1659
1660 }, {
1661 key: "placeCaretAtEnd",
1662 value: function placeCaretAtEnd(el) {
1663 el.focus();
1664
1665 if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
1666 var range = document.createRange();
1667 range.selectNodeContents(el);
1668 range.collapse(false);
1669 var sel = window.getSelection();
1670 sel.removeAllRanges();
1671 sel.addRange(range);
1672 } else if (typeof document.body.createTextRange != "undefined") {
1673 var textRange = document.body.createTextRange();
1674 textRange.moveToElementText(el);
1675 textRange.collapse(false);
1676 textRange.select();
1677 }
1678 } // for contenteditable
1679
1680 }, {
1681 key: "insertTextAtCursor",
1682 value: function insertTextAtCursor(text) {
1683 var sel, range;
1684 sel = window.getSelection();
1685 range = sel.getRangeAt(0);
1686 range.deleteContents();
1687 var textNode = document.createTextNode(text);
1688 range.insertNode(textNode);
1689 range.selectNodeContents(textNode);
1690 range.collapse(false);
1691 sel.removeAllRanges();
1692 sel.addRange(range);
1693 } // for regular inputs
1694
1695 }, {
1696 key: "insertAtCaret",
1697 value: function insertAtCaret(textarea, text) {
1698 var scrollPos = textarea.scrollTop;
1699 var caretPos = textarea.selectionStart;
1700 var front = textarea.value.substring(0, caretPos);
1701 var back = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
1702 textarea.value = front + text + back;
1703 caretPos = caretPos + text.length;
1704 textarea.selectionStart = caretPos;
1705 textarea.selectionEnd = caretPos;
1706 textarea.focus();
1707 textarea.scrollTop = scrollPos;
1708 }
1709 }, {
1710 key: "hideMenu",
1711 value: function hideMenu() {
1712 if (this.menu) {
1713 this.menu.style.cssText = "display: none;";
1714 this.isActive = false;
1715 this.menuSelected = 0;
1716 this.current = {};
1717 }
1718 }
1719 }, {
1720 key: "selectItemAtIndex",
1721 value: function selectItemAtIndex(index, originalEvent) {
1722 index = parseInt(index);
1723 if (typeof index !== "number" || isNaN(index)) return;
1724 var item = this.current.filteredItems[index];
1725 var content = this.current.collection.selectTemplate(item);
1726 if (content !== null) this.replaceText(content, originalEvent, item);
1727 }
1728 }, {
1729 key: "replaceText",
1730 value: function replaceText(content, originalEvent, item) {
1731 this.range.replaceTriggerText(content, true, true, originalEvent, item);
1732 }
1733 }, {
1734 key: "_append",
1735 value: function _append(collection, newValues, replace) {
1736 if (typeof collection.values === "function") {
1737 throw new Error("Unable to append to values, as it is a function.");
1738 } else if (!replace) {
1739 collection.values = collection.values.concat(newValues);
1740 } else {
1741 collection.values = newValues;
1742 }
1743 }
1744 }, {
1745 key: "append",
1746 value: function append(collectionIndex, newValues, replace) {
1747 var index = parseInt(collectionIndex);
1748 if (typeof index !== "number") throw new Error("please provide an index for the collection to update.");
1749 var collection = this.collection[index];
1750
1751 this._append(collection, newValues, replace);
1752 }
1753 }, {
1754 key: "appendCurrent",
1755 value: function appendCurrent(newValues, replace) {
1756 if (this.isActive) {
1757
1758 this._append(this.current.collection, newValues, replace);
1759 } else {
1760 throw new Error("No active state. Please use append instead and pass an index.");
1761 }
1762 }
1763 }, {
1764 key: "detach",
1765 value: function detach(el) {
1766 if (!el) {
1767 throw new Error("[Tribute] Must pass in a DOM node or NodeList.");
1768 } // Check if it is a jQuery collection
1769
1770
1771 if (typeof jQuery !== "undefined" && el instanceof jQuery) {
1772 el = el.get();
1773 } // Is el an Array/Array-like object?
1774
1775
1776 if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
1777 var length = el.length;
1778
1779 for (var i = 0; i < length; ++i) {
1780 this._detach(el[i]);
1781 }
1782 } else {
1783 this._detach(el);
1784 }
1785 }
1786 }, {
1787 key: "_detach",
1788 value: function _detach(el) {
1789 var _this3 = this;
1790
1791 this.events.unbind(el);
1792
1793 if (el.tributeMenu) {
1794 this.menuEvents.unbind(el.tributeMenu);
1795 }
1796
1797 setTimeout(function () {
1798 el.removeAttribute("data-tribute");
1799 _this3.isActive = false;
1800
1801 if (el.tributeMenu) {
1802 el.tributeMenu.remove();
1803 }
1804 });
1805 }
1806 }, {
1807 key: "isActive",
1808 get: function get() {
1809 return this._isActive;
1810 },
1811 set: function set(val) {
1812 if (this._isActive != val) {
1813 this._isActive = val;
1814
1815 if (this.current.element) {
1816 var noMatchEvent = new CustomEvent("tribute-active-".concat(val));
1817 this.current.element.dispatchEvent(noMatchEvent);
1818 }
1819 }
1820 }
1821 }], [{
1822 key: "defaultSelectTemplate",
1823 value: function defaultSelectTemplate(item) {
1824 if (typeof item === "undefined") return "".concat(this.current.collection.trigger).concat(this.current.mentionText);
1825
1826 if (this.range.isContentEditable(this.current.element)) {
1827 return '<span class="tribute-mention">' + (this.current.collection.trigger + item.original[this.current.collection.fillAttr]) + "</span>";
1828 }
1829
1830 return this.current.collection.trigger + item.original[this.current.collection.fillAttr];
1831 }
1832 }, {
1833 key: "defaultMenuItemTemplate",
1834 value: function defaultMenuItemTemplate(matchItem) {
1835 return matchItem.string;
1836 }
1837 }, {
1838 key: "inputTypes",
1839 value: function inputTypes() {
1840 return ["TEXTAREA", "INPUT"];
1841 }
1842 }]);
1843
1844 return Tribute;
1845 }();
1846
1847 /**
1848 * Tribute.js
1849 * Native ES6 JavaScript @mention Plugin
1850 **/
1851
1852 return Tribute;
1853
1854 })));
1855