PluginProbe
Social Media Auto Poster – Schedule & Publish to Buffer / 6.2.0
Social Media Auto Poster – Schedule & Publish to Buffer v6.2.0
6.2.4 6.2.3 6.2.2 6.2.1 6.2.0 6.1.2 6.1.1 6.1.0 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 6.0.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 All 125 releases
wp-to-buffer / lib / shared / js / tribute.js

tribute.js in Social Media Auto Poster – Schedule & Publish to Buffer 6.2.0, at lib/shared/js/tribute.js

1,854 lines 64.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 this._append(this.current.collection, newValues, replace);
1758 } else {
1759 throw new Error("No active state. Please use append instead and pass an index.");
1760 }
1761 }
1762 }, {
1763 key: "detach",
1764 value: function detach(el) {
1765 if (!el) {
1766 throw new Error("[Tribute] Must pass in a DOM node or NodeList.");
1767 } // Check if it is a jQuery collection
1768
1769
1770 if (typeof jQuery !== "undefined" && el instanceof jQuery) {
1771 el = el.get();
1772 } // Is el an Array/Array-like object?
1773
1774
1775 if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) {
1776 var length = el.length;
1777
1778 for (var i = 0; i < length; ++i) {
1779 this._detach(el[i]);
1780 }
1781 } else {
1782 this._detach(el);
1783 }
1784 }
1785 }, {
1786 key: "_detach",
1787 value: function _detach(el) {
1788 var _this3 = this;
1789
1790 this.events.unbind(el);
1791
1792 if (el.tributeMenu) {
1793 this.menuEvents.unbind(el.tributeMenu);
1794 }
1795
1796 setTimeout(function () {
1797 el.removeAttribute("data-tribute");
1798 _this3.isActive = false;
1799
1800 if (el.tributeMenu) {
1801 el.tributeMenu.remove();
1802 }
1803 });
1804 }
1805 }, {
1806 key: "isActive",
1807 get: function get() {
1808 return this._isActive;
1809 },
1810 set: function set(val) {
1811 if (this._isActive != val) {
1812 this._isActive = val;
1813
1814 if (this.current.element) {
1815 var noMatchEvent = new CustomEvent("tribute-active-".concat(val));
1816 this.current.element.dispatchEvent(noMatchEvent);
1817 }
1818 }
1819 }
1820 }], [{
1821 key: "defaultSelectTemplate",
1822 value: function defaultSelectTemplate(item) {
1823 if (typeof item === "undefined") return "".concat(this.current.collection.trigger).concat(this.current.mentionText);
1824
1825 if (this.range.isContentEditable(this.current.element)) {
1826 return '<span class="tribute-mention">' + (this.current.collection.trigger + item.original[this.current.collection.fillAttr]) + "</span>";
1827 }
1828
1829 return this.current.collection.trigger + item.original[this.current.collection.fillAttr];
1830 }
1831 }, {
1832 key: "defaultMenuItemTemplate",
1833 value: function defaultMenuItemTemplate(matchItem) {
1834 return matchItem.string;
1835 }
1836 }, {
1837 key: "inputTypes",
1838 value: function inputTypes() {
1839 return ["TEXTAREA", "INPUT"];
1840 }
1841 }]);
1842
1843 return Tribute;
1844 }();
1845
1846 /**
1847 * Tribute.js
1848 * Native ES6 JavaScript @mention Plugin
1849 **/
1850
1851 return Tribute;
1852
1853 })));
1854