| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  | if (!Array.prototype.find) { | 
					
						
							|  |  |  |     Array.prototype.find = function(predicate) { | 
					
						
							|  |  |  |         if (this === null) { | 
					
						
							|  |  |  |             throw new TypeError('Array.prototype.find called on null or undefined') | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (typeof predicate !== 'function') { | 
					
						
							|  |  |  |             throw new TypeError('predicate must be a function') | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         var list = Object(this); | 
					
						
							|  |  |  |         var length = list.length >>> 0; | 
					
						
							|  |  |  |         var thisArg = arguments[1]; | 
					
						
							|  |  |  |         var value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (var i = 0; i < length; i++) { | 
					
						
							|  |  |  |             value = list[i]; | 
					
						
							|  |  |  |             if (predicate.call(thisArg, value, i, list)) { | 
					
						
							|  |  |  |                 return value | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return undefined | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (window && typeof window.CustomEvent !== "function") { | 
					
						
							|  |  |  |   function CustomEvent$1(event, params) { | 
					
						
							|  |  |  |     params = params || { | 
					
						
							|  |  |  |       bubbles: false, | 
					
						
							|  |  |  |       cancelable: false, | 
					
						
							|  |  |  |       detail: undefined | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     var evt = document.createEvent('CustomEvent'); | 
					
						
							|  |  |  |     evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); | 
					
						
							|  |  |  |     return evt | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  if (typeof window.Event !== 'undefined') { | 
					
						
							|  |  |  |    CustomEvent$1.prototype = window.Event.prototype; | 
					
						
							|  |  |  |  } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   window.CustomEvent = CustomEvent$1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TributeEvents { | 
					
						
							|  |  |  |   constructor(tribute) { | 
					
						
							|  |  |  |     this.tribute = tribute; | 
					
						
							|  |  |  |     this.tribute.events = this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static keys() { | 
					
						
							|  |  |  |     return [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 9, | 
					
						
							|  |  |  |         value: "TAB" | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 8, | 
					
						
							|  |  |  |         value: "DELETE" | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 13, | 
					
						
							|  |  |  |         value: "ENTER" | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 27, | 
					
						
							|  |  |  |         value: "ESCAPE" | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 32, | 
					
						
							|  |  |  |         value: "SPACE" | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 38, | 
					
						
							|  |  |  |         value: "UP" | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: 40, | 
					
						
							|  |  |  |         value: "DOWN" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bind(element) { | 
					
						
							|  |  |  |     element.boundKeydown = this.keydown.bind(element, this); | 
					
						
							|  |  |  |     element.boundKeyup = this.keyup.bind(element, this); | 
					
						
							|  |  |  |     element.boundInput = this.input.bind(element, this); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     element.addEventListener("keydown", element.boundKeydown, false); | 
					
						
							|  |  |  |     element.addEventListener("keyup", element.boundKeyup, false); | 
					
						
							|  |  |  |     element.addEventListener("input", element.boundInput, false); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   unbind(element) { | 
					
						
							|  |  |  |     element.removeEventListener("keydown", element.boundKeydown, false); | 
					
						
							|  |  |  |     element.removeEventListener("keyup", element.boundKeyup, false); | 
					
						
							|  |  |  |     element.removeEventListener("input", element.boundInput, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete element.boundKeydown; | 
					
						
							|  |  |  |     delete element.boundKeyup; | 
					
						
							|  |  |  |     delete element.boundInput; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   keydown(instance, event) { | 
					
						
							|  |  |  |     if (instance.shouldDeactivate(event)) { | 
					
						
							|  |  |  |       instance.tribute.isActive = false; | 
					
						
							|  |  |  |       instance.tribute.hideMenu(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let element = this; | 
					
						
							|  |  |  |     instance.commandEvent = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TributeEvents.keys().forEach(o => { | 
					
						
							|  |  |  |       if (o.key === event.keyCode) { | 
					
						
							|  |  |  |         instance.commandEvent = true; | 
					
						
							|  |  |  |         instance.callbacks()[o.value.toLowerCase()](event, element); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   input(instance, event) { | 
					
						
							|  |  |  |     instance.inputEvent = true; | 
					
						
							|  |  |  |     instance.keyup.call(this, instance, event); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   click(instance, event) { | 
					
						
							|  |  |  |     let tribute = instance.tribute; | 
					
						
							|  |  |  |     if (tribute.menu && tribute.menu.contains(event.target)) { | 
					
						
							|  |  |  |       let li = event.target; | 
					
						
							|  |  |  |       event.preventDefault(); | 
					
						
							|  |  |  |       event.stopPropagation(); | 
					
						
							|  |  |  |       while (li.nodeName.toLowerCase() !== "li") { | 
					
						
							|  |  |  |         li = li.parentNode; | 
					
						
							|  |  |  |         if (!li || li === tribute.menu) { | 
					
						
							|  |  |  |           throw new Error("cannot find the <li> container for the click"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       tribute.selectItemAtIndex(li.getAttribute("data-index"), event); | 
					
						
							|  |  |  |       tribute.hideMenu(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO: should fire with externalTrigger and target is outside of menu
 | 
					
						
							|  |  |  |     } else if (tribute.current.element && !tribute.current.externalTrigger) { | 
					
						
							|  |  |  |       tribute.current.externalTrigger = false; | 
					
						
							|  |  |  |       setTimeout(() => tribute.hideMenu()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   keyup(instance, event) { | 
					
						
							|  |  |  |     if (instance.inputEvent) { | 
					
						
							|  |  |  |       instance.inputEvent = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     instance.updateSelection(this); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (event.keyCode === 27) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!instance.tribute.allowSpaces && instance.tribute.hasTrailingSpace) { | 
					
						
							|  |  |  |       instance.tribute.hasTrailingSpace = false; | 
					
						
							|  |  |  |       instance.commandEvent = true; | 
					
						
							|  |  |  |       instance.callbacks()["space"](event, this); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!instance.tribute.isActive) { | 
					
						
							|  |  |  |       if (instance.tribute.autocompleteMode) { | 
					
						
							|  |  |  |         instance.callbacks().triggerChar(event, this, ""); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         let keyCode = instance.getKeyCode(instance, this, event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isNaN(keyCode) || !keyCode) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let trigger = instance.tribute.triggers().find(trigger => { | 
					
						
							|  |  |  |           return trigger.charCodeAt(0) === keyCode; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (typeof trigger !== "undefined") { | 
					
						
							|  |  |  |           instance.callbacks().triggerChar(event, this, trigger); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       instance.tribute.current.mentionText.length < | 
					
						
							|  |  |  |       instance.tribute.current.collection.menuShowMinLength | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       ((instance.tribute.current.trigger || | 
					
						
							|  |  |  |         instance.tribute.autocompleteMode) && | 
					
						
							|  |  |  |         instance.commandEvent === false) || | 
					
						
							|  |  |  |       (instance.tribute.isActive && event.keyCode === 8) | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       instance.tribute.showMenuFor(this, true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shouldDeactivate(event) { | 
					
						
							|  |  |  |     if (!this.tribute.isActive) return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.tribute.current.mentionText.length === 0) { | 
					
						
							|  |  |  |       let eventKeyPressed = false; | 
					
						
							|  |  |  |       TributeEvents.keys().forEach(o => { | 
					
						
							|  |  |  |         if (event.keyCode === o.key) eventKeyPressed = true; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return !eventKeyPressed; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   getKeyCode(instance, el, event) { | 
					
						
							|  |  |  |     let tribute = instance.tribute; | 
					
						
							|  |  |  |     let info = tribute.range.getTriggerInfo( | 
					
						
							|  |  |  |       false, | 
					
						
							|  |  |  |       tribute.hasTrailingSpace, | 
					
						
							|  |  |  |       true, | 
					
						
							|  |  |  |       tribute.allowSpaces, | 
					
						
							|  |  |  |       tribute.autocompleteMode | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (info) { | 
					
						
							|  |  |  |       return info.mentionTriggerChar.charCodeAt(0); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   updateSelection(el) { | 
					
						
							|  |  |  |     this.tribute.current.element = el; | 
					
						
							|  |  |  |     let info = this.tribute.range.getTriggerInfo( | 
					
						
							|  |  |  |       false, | 
					
						
							|  |  |  |       this.tribute.hasTrailingSpace, | 
					
						
							|  |  |  |       true, | 
					
						
							|  |  |  |       this.tribute.allowSpaces, | 
					
						
							|  |  |  |       this.tribute.autocompleteMode | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (info) { | 
					
						
							|  |  |  |       this.tribute.current.selectedPath = info.mentionSelectedPath; | 
					
						
							|  |  |  |       this.tribute.current.mentionText = info.mentionText; | 
					
						
							|  |  |  |       this.tribute.current.selectedOffset = info.mentionSelectedOffset; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   callbacks() { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       triggerChar: (e, el, trigger) => { | 
					
						
							|  |  |  |         let tribute = this.tribute; | 
					
						
							|  |  |  |         tribute.current.trigger = trigger; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let collectionItem = tribute.collection.find(item => { | 
					
						
							|  |  |  |           return item.trigger === trigger; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tribute.current.collection = collectionItem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |           tribute.current.mentionText.length >= | 
					
						
							|  |  |  |             tribute.current.collection.menuShowMinLength && | 
					
						
							|  |  |  |           tribute.inputEvent | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |           tribute.showMenuFor(el, true); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       enter: (e, el) => { | 
					
						
							|  |  |  |         // choose selection
 | 
					
						
							|  |  |  |         if (this.tribute.isActive && this.tribute.current.filteredItems) { | 
					
						
							|  |  |  |           e.preventDefault(); | 
					
						
							|  |  |  |           e.stopPropagation(); | 
					
						
							|  |  |  |           setTimeout(() => { | 
					
						
							|  |  |  |             this.tribute.selectItemAtIndex(this.tribute.menuSelected, e); | 
					
						
							|  |  |  |             this.tribute.hideMenu(); | 
					
						
							|  |  |  |           }, 0); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       escape: (e, el) => { | 
					
						
							|  |  |  |         if (this.tribute.isActive) { | 
					
						
							|  |  |  |           e.preventDefault(); | 
					
						
							|  |  |  |           e.stopPropagation(); | 
					
						
							|  |  |  |           this.tribute.isActive = false; | 
					
						
							|  |  |  |           this.tribute.hideMenu(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       tab: (e, el) => { | 
					
						
							|  |  |  |         // choose first match
 | 
					
						
							|  |  |  |         this.callbacks().enter(e, el); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       space: (e, el) => { | 
					
						
							|  |  |  |         if (this.tribute.isActive) { | 
					
						
							|  |  |  |           if (this.tribute.spaceSelectsMatch) { | 
					
						
							|  |  |  |             this.callbacks().enter(e, el); | 
					
						
							|  |  |  |           } else if (!this.tribute.allowSpaces) { | 
					
						
							|  |  |  |             e.stopPropagation(); | 
					
						
							|  |  |  |             setTimeout(() => { | 
					
						
							|  |  |  |               this.tribute.hideMenu(); | 
					
						
							|  |  |  |               this.tribute.isActive = false; | 
					
						
							|  |  |  |             }, 0); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       up: (e, el) => { | 
					
						
							|  |  |  |         // navigate up ul
 | 
					
						
							|  |  |  |         if (this.tribute.isActive && this.tribute.current.filteredItems) { | 
					
						
							|  |  |  |           e.preventDefault(); | 
					
						
							|  |  |  |           e.stopPropagation(); | 
					
						
							|  |  |  |           let count = this.tribute.current.filteredItems.length, | 
					
						
							|  |  |  |             selected = this.tribute.menuSelected; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (count > selected && selected > 0) { | 
					
						
							|  |  |  |             this.tribute.menuSelected--; | 
					
						
							|  |  |  |             this.setActiveLi(); | 
					
						
							|  |  |  |           } else if (selected === 0) { | 
					
						
							|  |  |  |             this.tribute.menuSelected = count - 1; | 
					
						
							|  |  |  |             this.setActiveLi(); | 
					
						
							|  |  |  |             this.tribute.menu.scrollTop = this.tribute.menu.scrollHeight; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       down: (e, el) => { | 
					
						
							|  |  |  |         // navigate down ul
 | 
					
						
							|  |  |  |         if (this.tribute.isActive && this.tribute.current.filteredItems) { | 
					
						
							|  |  |  |           e.preventDefault(); | 
					
						
							|  |  |  |           e.stopPropagation(); | 
					
						
							|  |  |  |           let count = this.tribute.current.filteredItems.length - 1, | 
					
						
							|  |  |  |             selected = this.tribute.menuSelected; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (count > selected) { | 
					
						
							|  |  |  |             this.tribute.menuSelected++; | 
					
						
							|  |  |  |             this.setActiveLi(); | 
					
						
							|  |  |  |           } else if (count === selected) { | 
					
						
							|  |  |  |             this.tribute.menuSelected = 0; | 
					
						
							|  |  |  |             this.setActiveLi(); | 
					
						
							|  |  |  |             this.tribute.menu.scrollTop = 0; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       delete: (e, el) => { | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |           this.tribute.isActive && | 
					
						
							|  |  |  |           this.tribute.current.mentionText.length < 1 | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |           this.tribute.hideMenu(); | 
					
						
							|  |  |  |         } else if (this.tribute.isActive) { | 
					
						
							|  |  |  |           this.tribute.showMenuFor(el); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setActiveLi(index) { | 
					
						
							|  |  |  |     let lis = this.tribute.menu.querySelectorAll("li"), | 
					
						
							|  |  |  |       length = lis.length >>> 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (index) this.tribute.menuSelected = parseInt(index); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (let i = 0; i < length; i++) { | 
					
						
							|  |  |  |       let li = lis[i]; | 
					
						
							|  |  |  |       if (i === this.tribute.menuSelected) { | 
					
						
							|  |  |  |         li.classList.add(this.tribute.current.collection.selectClass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let liClientRect = li.getBoundingClientRect(); | 
					
						
							|  |  |  |         let menuClientRect = this.tribute.menu.getBoundingClientRect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (liClientRect.bottom > menuClientRect.bottom) { | 
					
						
							|  |  |  |           let scrollDistance = liClientRect.bottom - menuClientRect.bottom; | 
					
						
							|  |  |  |           this.tribute.menu.scrollTop += scrollDistance; | 
					
						
							|  |  |  |         } else if (liClientRect.top < menuClientRect.top) { | 
					
						
							|  |  |  |           let scrollDistance = menuClientRect.top - liClientRect.top; | 
					
						
							|  |  |  |           this.tribute.menu.scrollTop -= scrollDistance; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         li.classList.remove(this.tribute.current.collection.selectClass); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   getFullHeight(elem, includeMargin) { | 
					
						
							|  |  |  |     let height = elem.getBoundingClientRect().height; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (includeMargin) { | 
					
						
							|  |  |  |       let style = elem.currentStyle || window.getComputedStyle(elem); | 
					
						
							|  |  |  |       return ( | 
					
						
							|  |  |  |         height + parseFloat(style.marginTop) + parseFloat(style.marginBottom) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return height; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TributeMenuEvents { | 
					
						
							|  |  |  |   constructor(tribute) { | 
					
						
							|  |  |  |     this.tribute = tribute; | 
					
						
							|  |  |  |     this.tribute.menuEvents = this; | 
					
						
							|  |  |  |     this.menu = this.tribute.menu; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bind(menu) { | 
					
						
							|  |  |  |     this.menuClickEvent = this.tribute.events.click.bind(null, this); | 
					
						
							|  |  |  |     this.menuContainerScrollEvent = this.debounce( | 
					
						
							|  |  |  |       () => { | 
					
						
							|  |  |  |         if (this.tribute.isActive) { | 
					
						
							|  |  |  |           this.tribute.hideMenu(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       10, | 
					
						
							|  |  |  |       false | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     this.windowResizeEvent = this.debounce( | 
					
						
							|  |  |  |       () => { | 
					
						
							|  |  |  |         if (this.tribute.isActive) { | 
					
						
							|  |  |  |           this.tribute.hideMenu(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       10, | 
					
						
							|  |  |  |       false | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // fixes IE11 issues with mousedown
 | 
					
						
							|  |  |  |     this.tribute.range | 
					
						
							|  |  |  |       .getDocument() | 
					
						
							|  |  |  |       .addEventListener("MSPointerDown", this.menuClickEvent, false); | 
					
						
							|  |  |  |     this.tribute.range | 
					
						
							|  |  |  |       .getDocument() | 
					
						
							|  |  |  |       .addEventListener("mousedown", this.menuClickEvent, false); | 
					
						
							|  |  |  |     window.addEventListener("resize", this.windowResizeEvent); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.menuContainer) { | 
					
						
							|  |  |  |       this.menuContainer.addEventListener( | 
					
						
							|  |  |  |         "scroll", | 
					
						
							|  |  |  |         this.menuContainerScrollEvent, | 
					
						
							|  |  |  |         false | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       window.addEventListener("scroll", this.menuContainerScrollEvent); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   unbind(menu) { | 
					
						
							|  |  |  |     this.tribute.range | 
					
						
							|  |  |  |       .getDocument() | 
					
						
							|  |  |  |       .removeEventListener("mousedown", this.menuClickEvent, false); | 
					
						
							|  |  |  |     this.tribute.range | 
					
						
							|  |  |  |       .getDocument() | 
					
						
							|  |  |  |       .removeEventListener("MSPointerDown", this.menuClickEvent, false); | 
					
						
							|  |  |  |     window.removeEventListener("resize", this.windowResizeEvent); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.menuContainer) { | 
					
						
							|  |  |  |       this.menuContainer.removeEventListener( | 
					
						
							|  |  |  |         "scroll", | 
					
						
							|  |  |  |         this.menuContainerScrollEvent, | 
					
						
							|  |  |  |         false | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       window.removeEventListener("scroll", this.menuContainerScrollEvent); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   debounce(func, wait, immediate) { | 
					
						
							|  |  |  |     var timeout; | 
					
						
							|  |  |  |     return () => { | 
					
						
							|  |  |  |       var context = this, | 
					
						
							|  |  |  |         args = arguments; | 
					
						
							|  |  |  |       var later = () => { | 
					
						
							|  |  |  |         timeout = null; | 
					
						
							|  |  |  |         if (!immediate) func.apply(context, args); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       var callNow = immediate && !timeout; | 
					
						
							|  |  |  |       clearTimeout(timeout); | 
					
						
							|  |  |  |       timeout = setTimeout(later, wait); | 
					
						
							|  |  |  |       if (callNow) func.apply(context, args); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Thanks to https://github.com/jeff-collins/ment.io
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TributeRange { | 
					
						
							|  |  |  |     constructor(tribute) { | 
					
						
							|  |  |  |         this.tribute = tribute; | 
					
						
							|  |  |  |         this.tribute.range = this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getDocument() { | 
					
						
							| 
									
										
										
										
											2024-06-06 20:05:24 -04:00
										 |  |  |         return document; | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     positionMenuAtCaret(scrollTo) { | 
					
						
							|  |  |  |         let context = this.tribute.current, | 
					
						
							|  |  |  |             coordinates; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let info = this.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces, this.tribute.autocompleteMode); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (typeof info !== 'undefined') { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if(!this.tribute.positionMenu){ | 
					
						
							|  |  |  |                 this.tribute.menu.style.cssText = `display: block;`; | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!this.isContentEditable(context.element)) { | 
					
						
							|  |  |  |                 coordinates = this.getTextAreaOrInputUnderlinePosition(this.tribute.current.element, | 
					
						
							|  |  |  |                     info.mentionPosition); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 coordinates = this.getContentEditableCaretPosition(info.mentionPosition); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this.tribute.menu.style.cssText = `top: ${coordinates.top}px;
 | 
					
						
							|  |  |  |                                      left: ${coordinates.left}px; | 
					
						
							|  |  |  |                                      right: ${coordinates.right}px; | 
					
						
							|  |  |  |                                      bottom: ${coordinates.bottom}px; | 
					
						
							|  |  |  |                                      max-height: ${coordinates.maxHeight || 500}px; | 
					
						
							|  |  |  |                                      max-width: ${coordinates.maxWidth || 300}px; | 
					
						
							|  |  |  |                                      position: ${coordinates.position || 'absolute'}; | 
					
						
							|  |  |  |                                      display: block;`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (coordinates.left === 'auto') { | 
					
						
							|  |  |  |                 this.tribute.menu.style.left = 'auto'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (coordinates.top === 'auto') { | 
					
						
							|  |  |  |                 this.tribute.menu.style.top = 'auto'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (scrollTo) this.scrollIntoView(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             this.tribute.menu.style.cssText = 'display: none'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     get menuContainerIsBody() { | 
					
						
							|  |  |  |         return this.tribute.menuContainer === document.body || !this.tribute.menuContainer; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     selectElement(targetElement, path, offset) { | 
					
						
							|  |  |  |         let range; | 
					
						
							|  |  |  |         let elem = targetElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (path) { | 
					
						
							|  |  |  |             for (var i = 0; i < path.length; i++) { | 
					
						
							|  |  |  |                 elem = elem.childNodes[path[i]]; | 
					
						
							|  |  |  |                 if (elem === undefined) { | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 while (elem.length < offset) { | 
					
						
							|  |  |  |                     offset -= elem.length; | 
					
						
							|  |  |  |                     elem = elem.nextSibling; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (elem.childNodes.length === 0 && !elem.length) { | 
					
						
							|  |  |  |                     elem = elem.previousSibling; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         let sel = this.getWindowSelection(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         range = this.getDocument().createRange(); | 
					
						
							|  |  |  |         range.setStart(elem, offset); | 
					
						
							|  |  |  |         range.setEnd(elem, offset); | 
					
						
							|  |  |  |         range.collapse(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             sel.removeAllRanges(); | 
					
						
							|  |  |  |         } catch (error) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sel.addRange(range); | 
					
						
							|  |  |  |         targetElement.focus(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     replaceTriggerText(text, requireLeadingSpace, hasTrailingSpace, originalEvent, item) { | 
					
						
							|  |  |  |         let info = this.getTriggerInfo(true, hasTrailingSpace, requireLeadingSpace, this.tribute.allowSpaces, this.tribute.autocompleteMode); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (info !== undefined) { | 
					
						
							|  |  |  |             let context = this.tribute.current; | 
					
						
							|  |  |  |             let replaceEvent = new CustomEvent('tribute-replaced', { | 
					
						
							|  |  |  |                 detail: { | 
					
						
							|  |  |  |                     item: item, | 
					
						
							|  |  |  |                     instance: context, | 
					
						
							|  |  |  |                     context: info, | 
					
						
							|  |  |  |                     event: originalEvent, | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!this.isContentEditable(context.element)) { | 
					
						
							|  |  |  |                 let myField = this.tribute.current.element; | 
					
						
							|  |  |  |                 let textSuffix = typeof this.tribute.replaceTextSuffix == 'string' | 
					
						
							|  |  |  |                     ? this.tribute.replaceTextSuffix | 
					
						
							|  |  |  |                     : ' '; | 
					
						
							|  |  |  |                 text += textSuffix; | 
					
						
							|  |  |  |                 let startPos = info.mentionPosition; | 
					
						
							|  |  |  |                 let endPos = info.mentionPosition + info.mentionText.length + textSuffix.length; | 
					
						
							|  |  |  |                 if (!this.tribute.autocompleteMode) { | 
					
						
							|  |  |  |                     endPos += info.mentionTriggerChar.length - 1; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 myField.value = myField.value.substring(0, startPos) + text + | 
					
						
							|  |  |  |                     myField.value.substring(endPos, myField.value.length); | 
					
						
							|  |  |  |                 myField.selectionStart = startPos + text.length; | 
					
						
							|  |  |  |                 myField.selectionEnd = startPos + text.length; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // add a space to the end of the pasted text
 | 
					
						
							|  |  |  |                 let textSuffix = typeof this.tribute.replaceTextSuffix == 'string' | 
					
						
							|  |  |  |                     ? this.tribute.replaceTextSuffix | 
					
						
							|  |  |  |                     : '\xA0'; | 
					
						
							|  |  |  |                 text += textSuffix; | 
					
						
							|  |  |  |                 let endPos = info.mentionPosition + info.mentionText.length; | 
					
						
							|  |  |  |                 if (!this.tribute.autocompleteMode) { | 
					
						
							|  |  |  |                     endPos += info.mentionTriggerChar.length; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 this.pasteHtml(text, info.mentionPosition, endPos); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             context.element.dispatchEvent(new CustomEvent('input', { bubbles: true })); | 
					
						
							|  |  |  |             context.element.dispatchEvent(replaceEvent); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pasteHtml(html, startPos, endPos) { | 
					
						
							|  |  |  |         let range, sel; | 
					
						
							|  |  |  |         sel = this.getWindowSelection(); | 
					
						
							|  |  |  |         range = this.getDocument().createRange(); | 
					
						
							|  |  |  |         range.setStart(sel.anchorNode, startPos); | 
					
						
							|  |  |  |         range.setEnd(sel.anchorNode, endPos); | 
					
						
							|  |  |  |         range.deleteContents(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let el = this.getDocument().createElement('div'); | 
					
						
							|  |  |  |         el.innerHTML = html; | 
					
						
							|  |  |  |         let frag = this.getDocument().createDocumentFragment(), | 
					
						
							|  |  |  |             node, lastNode; | 
					
						
							|  |  |  |         while ((node = el.firstChild)) { | 
					
						
							|  |  |  |             lastNode = frag.appendChild(node); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         range.insertNode(frag); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Preserve the selection
 | 
					
						
							|  |  |  |         if (lastNode) { | 
					
						
							|  |  |  |             range = range.cloneRange(); | 
					
						
							|  |  |  |             range.setStartAfter(lastNode); | 
					
						
							|  |  |  |             range.collapse(true); | 
					
						
							|  |  |  |             sel.removeAllRanges(); | 
					
						
							|  |  |  |             sel.addRange(range); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getWindowSelection() { | 
					
						
							| 
									
										
										
										
											2024-06-06 20:05:24 -04:00
										 |  |  |         if (this.tribute.collection[0].iframe?.getSelection) { | 
					
						
							| 
									
										
										
										
											2024-06-06 19:52:37 -04:00
										 |  |  |             return this.tribute.collection[0].iframe.getSelection() | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return window.getSelection() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getNodePositionInParent(element) { | 
					
						
							|  |  |  |         if (element.parentNode === null) { | 
					
						
							|  |  |  |             return 0 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (var i = 0; i < element.parentNode.childNodes.length; i++) { | 
					
						
							|  |  |  |             let node = element.parentNode.childNodes[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (node === element) { | 
					
						
							|  |  |  |                 return i | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getContentEditableSelectedPath(ctx) { | 
					
						
							|  |  |  |         let sel = this.getWindowSelection(); | 
					
						
							|  |  |  |         let selected = sel.anchorNode; | 
					
						
							|  |  |  |         let path = []; | 
					
						
							|  |  |  |         let offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (selected != null) { | 
					
						
							|  |  |  |             let i; | 
					
						
							|  |  |  |             let ce = selected.contentEditable; | 
					
						
							|  |  |  |             while (selected !== null && ce !== 'true') { | 
					
						
							|  |  |  |                 i = this.getNodePositionInParent(selected); | 
					
						
							|  |  |  |                 path.push(i); | 
					
						
							|  |  |  |                 selected = selected.parentNode; | 
					
						
							|  |  |  |                 if (selected !== null) { | 
					
						
							|  |  |  |                     ce = selected.contentEditable; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             path.reverse(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // getRangeAt may not exist, need alternative
 | 
					
						
							|  |  |  |             offset = sel.getRangeAt(0).startOffset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |                 selected: selected, | 
					
						
							|  |  |  |                 path: path, | 
					
						
							|  |  |  |                 offset: offset | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getTextPrecedingCurrentSelection() { | 
					
						
							|  |  |  |         let context = this.tribute.current, | 
					
						
							|  |  |  |             text = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!this.isContentEditable(context.element)) { | 
					
						
							|  |  |  |             let textComponent = this.tribute.current.element; | 
					
						
							|  |  |  |             if (textComponent) { | 
					
						
							|  |  |  |                 let startPos = textComponent.selectionStart; | 
					
						
							|  |  |  |                 if (textComponent.value && startPos >= 0) { | 
					
						
							|  |  |  |                     text = textComponent.value.substring(0, startPos); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             let selectedElem = this.getWindowSelection().anchorNode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (selectedElem != null) { | 
					
						
							|  |  |  |                 let workingNodeContent = selectedElem.textContent; | 
					
						
							|  |  |  |                 let selectStartOffset = this.getWindowSelection().getRangeAt(0).startOffset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (workingNodeContent && selectStartOffset >= 0) { | 
					
						
							|  |  |  |                     text = workingNodeContent.substring(0, selectStartOffset); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return text | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getLastWordInText(text) { | 
					
						
							|  |  |  |         text = text.replace(/\u00A0/g, ' '); // https://stackoverflow.com/questions/29850407/how-do-i-replace-unicode-character-u00a0-with-a-space-in-javascript
 | 
					
						
							|  |  |  |         var wordsArray; | 
					
						
							|  |  |  |         if (this.tribute.autocompleteSeparator) { | 
					
						
							|  |  |  |             wordsArray = text.split(this.tribute.autocompleteSeparator); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             wordsArray = text.split(/\s+/); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         var worldsCount = wordsArray.length - 1; | 
					
						
							|  |  |  |         return wordsArray[worldsCount].trim(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getTriggerInfo(menuAlreadyActive, hasTrailingSpace, requireLeadingSpace, allowSpaces, isAutocomplete) { | 
					
						
							|  |  |  |         let ctx = this.tribute.current; | 
					
						
							|  |  |  |         let selected, path, offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!this.isContentEditable(ctx.element)) { | 
					
						
							|  |  |  |             selected = this.tribute.current.element; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             let selectionInfo = this.getContentEditableSelectedPath(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (selectionInfo) { | 
					
						
							|  |  |  |                 selected = selectionInfo.selected; | 
					
						
							|  |  |  |                 path = selectionInfo.path; | 
					
						
							|  |  |  |                 offset = selectionInfo.offset; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let effectiveRange = this.getTextPrecedingCurrentSelection(); | 
					
						
							|  |  |  |         let lastWordOfEffectiveRange = this.getLastWordInText(effectiveRange); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isAutocomplete) { | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |                 mentionPosition: effectiveRange.length - lastWordOfEffectiveRange.length, | 
					
						
							|  |  |  |                 mentionText: lastWordOfEffectiveRange, | 
					
						
							|  |  |  |                 mentionSelectedElement: selected, | 
					
						
							|  |  |  |                 mentionSelectedPath: path, | 
					
						
							|  |  |  |                 mentionSelectedOffset: offset | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (effectiveRange !== undefined && effectiveRange !== null) { | 
					
						
							|  |  |  |             let mostRecentTriggerCharPos = -1; | 
					
						
							|  |  |  |             let triggerChar; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this.tribute.collection.forEach(config => { | 
					
						
							|  |  |  |                 let c = config.trigger; | 
					
						
							|  |  |  |                 let idx = config.requireLeadingSpace ? | 
					
						
							|  |  |  |                     this.lastIndexWithLeadingSpace(effectiveRange, c) : | 
					
						
							|  |  |  |                     effectiveRange.lastIndexOf(c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (idx > mostRecentTriggerCharPos) { | 
					
						
							|  |  |  |                     mostRecentTriggerCharPos = idx; | 
					
						
							|  |  |  |                     triggerChar = c; | 
					
						
							|  |  |  |                     requireLeadingSpace = config.requireLeadingSpace; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (mostRecentTriggerCharPos >= 0 && | 
					
						
							|  |  |  |                 ( | 
					
						
							|  |  |  |                     mostRecentTriggerCharPos === 0 || | 
					
						
							|  |  |  |                     !requireLeadingSpace || | 
					
						
							|  |  |  |                     /[\xA0\s]/g.test( | 
					
						
							|  |  |  |                         effectiveRange.substring( | 
					
						
							|  |  |  |                             mostRecentTriggerCharPos - 1, | 
					
						
							|  |  |  |                             mostRecentTriggerCharPos) | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) { | 
					
						
							|  |  |  |                 let currentTriggerSnippet = effectiveRange.substring(mostRecentTriggerCharPos + triggerChar.length, | 
					
						
							|  |  |  |                     effectiveRange.length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 triggerChar = effectiveRange.substring(mostRecentTriggerCharPos, mostRecentTriggerCharPos + triggerChar.length); | 
					
						
							|  |  |  |                 let firstSnippetChar = currentTriggerSnippet.substring(0, 1); | 
					
						
							|  |  |  |                 let leadingSpace = currentTriggerSnippet.length > 0 && | 
					
						
							|  |  |  |                     ( | 
					
						
							|  |  |  |                         firstSnippetChar === ' ' || | 
					
						
							|  |  |  |                         firstSnippetChar === '\xA0' | 
					
						
							|  |  |  |                     ); | 
					
						
							|  |  |  |                 if (hasTrailingSpace) { | 
					
						
							|  |  |  |                     currentTriggerSnippet = currentTriggerSnippet.trim(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 let regex = allowSpaces ? /[^\S ]/g : /[\xA0\s]/g; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 this.tribute.hasTrailingSpace = regex.test(currentTriggerSnippet); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (!leadingSpace && (menuAlreadyActive || !(regex.test(currentTriggerSnippet)))) { | 
					
						
							|  |  |  |                     return { | 
					
						
							|  |  |  |                         mentionPosition: mostRecentTriggerCharPos, | 
					
						
							|  |  |  |                         mentionText: currentTriggerSnippet, | 
					
						
							|  |  |  |                         mentionSelectedElement: selected, | 
					
						
							|  |  |  |                         mentionSelectedPath: path, | 
					
						
							|  |  |  |                         mentionSelectedOffset: offset, | 
					
						
							|  |  |  |                         mentionTriggerChar: triggerChar | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     lastIndexWithLeadingSpace (str, trigger) { | 
					
						
							|  |  |  |         let reversedStr = str.split('').reverse().join(''); | 
					
						
							|  |  |  |         let index = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (let cidx = 0, len = str.length; cidx < len; cidx++) { | 
					
						
							|  |  |  |             let firstChar = cidx === str.length - 1; | 
					
						
							|  |  |  |             let leadingSpace = /\s/.test(reversedStr[cidx + 1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             let match = true; | 
					
						
							|  |  |  |             for (let triggerIdx = trigger.length - 1; triggerIdx >= 0; triggerIdx--) { | 
					
						
							|  |  |  |               if (trigger[triggerIdx] !== reversedStr[cidx-triggerIdx]) { | 
					
						
							|  |  |  |                 match = false; | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (match && (firstChar || leadingSpace)) { | 
					
						
							|  |  |  |                 index = str.length - 1 - cidx; | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return index | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     isContentEditable(element) { | 
					
						
							|  |  |  |         return element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA' | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     isMenuOffScreen(coordinates, menuDimensions) { | 
					
						
							|  |  |  |         let windowWidth = window.innerWidth; | 
					
						
							|  |  |  |         let windowHeight = window.innerHeight; | 
					
						
							|  |  |  |         let doc = document.documentElement; | 
					
						
							|  |  |  |         let windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); | 
					
						
							|  |  |  |         let windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let menuTop = typeof coordinates.top === 'number' ? coordinates.top : windowTop + windowHeight - coordinates.bottom - menuDimensions.height; | 
					
						
							|  |  |  |         let menuRight = typeof coordinates.right === 'number' ? coordinates.right : coordinates.left + menuDimensions.width; | 
					
						
							|  |  |  |         let menuBottom = typeof coordinates.bottom === 'number' ? coordinates.bottom : coordinates.top + menuDimensions.height; | 
					
						
							|  |  |  |         let menuLeft = typeof coordinates.left === 'number' ? coordinates.left : windowLeft + windowWidth - coordinates.right - menuDimensions.width; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             top: menuTop < Math.floor(windowTop), | 
					
						
							|  |  |  |             right: menuRight > Math.ceil(windowLeft + windowWidth), | 
					
						
							|  |  |  |             bottom: menuBottom > Math.ceil(windowTop + windowHeight), | 
					
						
							|  |  |  |             left: menuLeft < Math.floor(windowLeft) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getMenuDimensions() { | 
					
						
							|  |  |  |         // Width of the menu depends of its contents and position
 | 
					
						
							|  |  |  |         // We must check what its width would be without any obstruction
 | 
					
						
							|  |  |  |         // This way, we can achieve good positioning for flipping the menu
 | 
					
						
							|  |  |  |         let dimensions = { | 
					
						
							|  |  |  |             width: null, | 
					
						
							|  |  |  |             height: null | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.tribute.menu.style.cssText = `top: 0px;
 | 
					
						
							|  |  |  |                                  left: 0px; | 
					
						
							|  |  |  |                                  position: fixed; | 
					
						
							|  |  |  |                                  display: block; | 
					
						
							|  |  |  |                                  visibility; hidden; | 
					
						
							|  |  |  |                                  max-height:500px;`;
 | 
					
						
							|  |  |  |        dimensions.width = this.tribute.menu.offsetWidth; | 
					
						
							|  |  |  |        dimensions.height = this.tribute.menu.offsetHeight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        this.tribute.menu.style.cssText = `display: none;`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        return dimensions | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getTextAreaOrInputUnderlinePosition(element, position, flipped) { | 
					
						
							|  |  |  |         let 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' | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let div = this.getDocument().createElement('div'); | 
					
						
							|  |  |  |         div.id = 'input-textarea-caret-position-mirror-div'; | 
					
						
							|  |  |  |         this.getDocument().body.appendChild(div); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let style = div.style; | 
					
						
							|  |  |  |         let computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         style.whiteSpace = 'pre-wrap'; | 
					
						
							|  |  |  |         if (element.nodeName !== 'INPUT') { | 
					
						
							|  |  |  |             style.wordWrap = 'break-word'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         style.position = 'absolute'; | 
					
						
							|  |  |  |         style.visibility = 'hidden'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // transfer the element's properties to the div
 | 
					
						
							|  |  |  |         properties.forEach(prop => { | 
					
						
							|  |  |  |             style[prop] = computed[prop]; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //NOT SURE WHY THIS IS HERE AND IT DOESNT SEEM HELPFUL
 | 
					
						
							|  |  |  |         // if (isFirefox) {
 | 
					
						
							|  |  |  |         //     style.width = `${(parseInt(computed.width) - 2)}px`
 | 
					
						
							|  |  |  |         //     if (element.scrollHeight > parseInt(computed.height))
 | 
					
						
							|  |  |  |         //         style.overflowY = 'scroll'
 | 
					
						
							|  |  |  |         // } else {
 | 
					
						
							|  |  |  |         //     style.overflow = 'hidden'
 | 
					
						
							|  |  |  |         // }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let span0 = document.createElement('span'); | 
					
						
							|  |  |  |         span0.textContent =  element.value.substring(0, position); | 
					
						
							|  |  |  |         div.appendChild(span0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (element.nodeName === 'INPUT') { | 
					
						
							|  |  |  |             div.textContent = div.textContent.replace(/\s/g, ' '); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //Create a span in the div that represents where the cursor
 | 
					
						
							|  |  |  |         //should be
 | 
					
						
							|  |  |  |         let span = this.getDocument().createElement('span'); | 
					
						
							|  |  |  |         //we give it no content as this represents the cursor
 | 
					
						
							|  |  |  |         span.textContent = '​'; | 
					
						
							|  |  |  |         div.appendChild(span); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let span2 = this.getDocument().createElement('span'); | 
					
						
							|  |  |  |         span2.textContent = element.value.substring(position); | 
					
						
							|  |  |  |         div.appendChild(span2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let rect = element.getBoundingClientRect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //position the div exactly over the element
 | 
					
						
							|  |  |  |         //so we can get the bounding client rect for the span and
 | 
					
						
							|  |  |  |         //it should represent exactly where the cursor is
 | 
					
						
							|  |  |  |         div.style.position = 'fixed'; | 
					
						
							|  |  |  |         div.style.left = rect.left + 'px'; | 
					
						
							|  |  |  |         div.style.top = rect.top + 'px'; | 
					
						
							|  |  |  |         div.style.width = rect.width + 'px'; | 
					
						
							|  |  |  |         div.style.height = rect.height + 'px'; | 
					
						
							|  |  |  |         div.scrollTop = element.scrollTop; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var spanRect = span.getBoundingClientRect(); | 
					
						
							|  |  |  |         this.getDocument().body.removeChild(div); | 
					
						
							|  |  |  |         return this.getFixedCoordinatesRelativeToRect(spanRect); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getContentEditableCaretPosition(selectedNodePosition) { | 
					
						
							|  |  |  |         let range; | 
					
						
							|  |  |  |         let sel = this.getWindowSelection(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         range = this.getDocument().createRange(); | 
					
						
							|  |  |  |         range.setStart(sel.anchorNode, selectedNodePosition); | 
					
						
							|  |  |  |         range.setEnd(sel.anchorNode, selectedNodePosition); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         range.collapse(false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let rect = range.getBoundingClientRect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return this.getFixedCoordinatesRelativeToRect(rect); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getFixedCoordinatesRelativeToRect(rect) { | 
					
						
							|  |  |  |         let coordinates = { | 
					
						
							|  |  |  |             position: 'fixed', | 
					
						
							|  |  |  |             left: rect.left, | 
					
						
							|  |  |  |             top: rect.top + rect.height | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let menuDimensions = this.getMenuDimensions(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var availableSpaceOnTop = rect.top; | 
					
						
							|  |  |  |         var availableSpaceOnBottom = window.innerHeight - (rect.top + rect.height); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //check to see where's the right place to put the menu vertically
 | 
					
						
							|  |  |  |         if (availableSpaceOnBottom < menuDimensions.height) { | 
					
						
							|  |  |  |           if (availableSpaceOnTop >= menuDimensions.height || availableSpaceOnTop > availableSpaceOnBottom) { | 
					
						
							|  |  |  |             coordinates.top = 'auto'; | 
					
						
							|  |  |  |             coordinates.bottom = window.innerHeight - rect.top; | 
					
						
							|  |  |  |             if (availableSpaceOnBottom < menuDimensions.height) { | 
					
						
							|  |  |  |               coordinates.maxHeight = availableSpaceOnTop; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             if (availableSpaceOnTop < menuDimensions.height) { | 
					
						
							|  |  |  |               coordinates.maxHeight = availableSpaceOnBottom; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var availableSpaceOnLeft = rect.left; | 
					
						
							|  |  |  |         var availableSpaceOnRight = window.innerWidth - rect.left; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //check to see where's the right place to put the menu horizontally
 | 
					
						
							|  |  |  |         if (availableSpaceOnRight < menuDimensions.width) { | 
					
						
							|  |  |  |           if (availableSpaceOnLeft >= menuDimensions.width || availableSpaceOnLeft > availableSpaceOnRight) { | 
					
						
							|  |  |  |             coordinates.left = 'auto'; | 
					
						
							|  |  |  |             coordinates.right = window.innerWidth - rect.left; | 
					
						
							|  |  |  |             if (availableSpaceOnRight < menuDimensions.width) { | 
					
						
							|  |  |  |               coordinates.maxWidth = availableSpaceOnLeft; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             if (availableSpaceOnLeft < menuDimensions.width) { | 
					
						
							|  |  |  |               coordinates.maxWidth = availableSpaceOnRight; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return coordinates | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     scrollIntoView(elem) { | 
					
						
							|  |  |  |         let reasonableBuffer = 20, | 
					
						
							|  |  |  |             clientRect; | 
					
						
							|  |  |  |         let maxScrollDisplacement = 100; | 
					
						
							|  |  |  |         let e = this.menu; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (typeof e === 'undefined') return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while (clientRect === undefined || clientRect.height === 0) { | 
					
						
							|  |  |  |             clientRect = e.getBoundingClientRect(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (clientRect.height === 0) { | 
					
						
							|  |  |  |                 e = e.childNodes[0]; | 
					
						
							|  |  |  |                 if (e === undefined || !e.getBoundingClientRect) { | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let elemTop = clientRect.top; | 
					
						
							|  |  |  |         let elemBottom = elemTop + clientRect.height; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (elemTop < 0) { | 
					
						
							|  |  |  |             window.scrollTo(0, window.pageYOffset + clientRect.top - reasonableBuffer); | 
					
						
							|  |  |  |         } else if (elemBottom > window.innerHeight) { | 
					
						
							|  |  |  |             let maxY = window.pageYOffset + clientRect.top - reasonableBuffer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (maxY - window.pageYOffset > maxScrollDisplacement) { | 
					
						
							|  |  |  |                 maxY = window.pageYOffset + maxScrollDisplacement; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             let targetY = window.pageYOffset - (window.innerHeight - elemBottom); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (targetY > maxY) { | 
					
						
							|  |  |  |                 targetY = maxY; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             window.scrollTo(0, targetY); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Thanks to https://github.com/mattyork/fuzzy
 | 
					
						
							|  |  |  | class TributeSearch { | 
					
						
							|  |  |  |     constructor(tribute) { | 
					
						
							|  |  |  |         this.tribute = tribute; | 
					
						
							|  |  |  |         this.tribute.search = this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     simpleFilter(pattern, array) { | 
					
						
							|  |  |  |         return array.filter(string => { | 
					
						
							|  |  |  |             return this.test(pattern, string) | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test(pattern, string) { | 
					
						
							|  |  |  |         return this.match(pattern, string) !== null | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     match(pattern, string, opts) { | 
					
						
							|  |  |  |         opts = opts || {}; | 
					
						
							|  |  |  |         let len = string.length, | 
					
						
							|  |  |  |             pre = opts.pre || '', | 
					
						
							|  |  |  |             post = opts.post || '', | 
					
						
							|  |  |  |             compareString = opts.caseSensitive && string || string.toLowerCase(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (opts.skip) { | 
					
						
							|  |  |  |             return {rendered: string, score: 0} | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pattern = opts.caseSensitive && pattern || pattern.toLowerCase(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let patternCache = this.traverse(compareString, pattern, 0, 0, []); | 
					
						
							|  |  |  |         if (!patternCache) { | 
					
						
							|  |  |  |             return null | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             rendered: this.render(string, patternCache.cache, pre, post), | 
					
						
							|  |  |  |             score: patternCache.score | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     traverse(string, pattern, stringIndex, patternIndex, patternCache) { | 
					
						
							|  |  |  |         if (this.tribute.autocompleteSeparator) { | 
					
						
							|  |  |  |             // if the pattern search at end
 | 
					
						
							|  |  |  |             pattern = pattern.split(this.tribute.autocompleteSeparator).splice(-1)[0]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (pattern.length === patternIndex) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // calculate score and copy the cache containing the indices where it's found
 | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |                 score: this.calculateScore(patternCache), | 
					
						
							|  |  |  |                 cache: patternCache.slice() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // if string at end or remaining pattern > remaining string
 | 
					
						
							|  |  |  |         if (string.length === stringIndex || pattern.length - patternIndex > string.length - stringIndex) { | 
					
						
							|  |  |  |             return undefined | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let c = pattern[patternIndex]; | 
					
						
							|  |  |  |         let index = string.indexOf(c, stringIndex); | 
					
						
							|  |  |  |         let best, temp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while (index > -1) { | 
					
						
							|  |  |  |             patternCache.push(index); | 
					
						
							|  |  |  |             temp = this.traverse(string, pattern, index + 1, patternIndex + 1, patternCache); | 
					
						
							|  |  |  |             patternCache.pop(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // if downstream traversal failed, return best answer so far
 | 
					
						
							|  |  |  |             if (!temp) { | 
					
						
							|  |  |  |                 return best | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!best || best.score < temp.score) { | 
					
						
							|  |  |  |                 best = temp; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             index = string.indexOf(c, index + 1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return best | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     calculateScore(patternCache) { | 
					
						
							|  |  |  |         let score = 0; | 
					
						
							|  |  |  |         let temp = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         patternCache.forEach((index, i) => { | 
					
						
							|  |  |  |             if (i > 0) { | 
					
						
							|  |  |  |                 if (patternCache[i - 1] + 1 === index) { | 
					
						
							|  |  |  |                     temp += temp + 1; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else { | 
					
						
							|  |  |  |                     temp = 1; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             score += temp; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return score | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     render(string, indices, pre, post) { | 
					
						
							|  |  |  |         var rendered = string.substring(0, indices[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         indices.forEach((index, i) => { | 
					
						
							|  |  |  |             rendered += pre + string[index] + post + | 
					
						
							|  |  |  |                 string.substring(index + 1, (indices[i + 1]) ? indices[i + 1] : string.length); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return rendered | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     filter(pattern, arr, opts) { | 
					
						
							|  |  |  |         opts = opts || {}; | 
					
						
							|  |  |  |         return arr | 
					
						
							|  |  |  |             .reduce((prev, element, idx, arr) => { | 
					
						
							|  |  |  |                 let str = element; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (opts.extract) { | 
					
						
							|  |  |  |                     str = opts.extract(element); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if (!str) { // take care of undefineds / nulls / etc.
 | 
					
						
							|  |  |  |                         str = ''; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 let rendered = this.match(pattern, str, opts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (rendered != null) { | 
					
						
							|  |  |  |                     prev[prev.length] = { | 
					
						
							|  |  |  |                         string: rendered.rendered, | 
					
						
							|  |  |  |                         score: rendered.score, | 
					
						
							|  |  |  |                         index: idx, | 
					
						
							|  |  |  |                         original: element | 
					
						
							|  |  |  |                     }; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return prev | 
					
						
							|  |  |  |             }, []) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         .sort((a, b) => { | 
					
						
							|  |  |  |             let compare = b.score - a.score; | 
					
						
							|  |  |  |             if (compare) return compare | 
					
						
							|  |  |  |             return a.index - b.index | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Tribute { | 
					
						
							|  |  |  |   constructor({ | 
					
						
							|  |  |  |     values = null, | 
					
						
							|  |  |  |     loadingItemTemplate = null, | 
					
						
							|  |  |  |     iframe = null, | 
					
						
							|  |  |  |     selectClass = "highlight", | 
					
						
							|  |  |  |     containerClass = "tribute-container", | 
					
						
							|  |  |  |     itemClass = "", | 
					
						
							|  |  |  |     trigger = "@", | 
					
						
							|  |  |  |     autocompleteMode = false, | 
					
						
							|  |  |  |     autocompleteSeparator = null, | 
					
						
							|  |  |  |     selectTemplate = null, | 
					
						
							|  |  |  |     menuItemTemplate = null, | 
					
						
							|  |  |  |     lookup = "key", | 
					
						
							|  |  |  |     fillAttr = "value", | 
					
						
							|  |  |  |     collection = null, | 
					
						
							|  |  |  |     menuContainer = null, | 
					
						
							|  |  |  |     noMatchTemplate = null, | 
					
						
							|  |  |  |     requireLeadingSpace = true, | 
					
						
							|  |  |  |     allowSpaces = false, | 
					
						
							|  |  |  |     replaceTextSuffix = null, | 
					
						
							|  |  |  |     positionMenu = true, | 
					
						
							|  |  |  |     spaceSelectsMatch = false, | 
					
						
							|  |  |  |     searchOpts = {}, | 
					
						
							|  |  |  |     menuItemLimit = null, | 
					
						
							|  |  |  |     menuShowMinLength = 0 | 
					
						
							|  |  |  |   }) { | 
					
						
							|  |  |  |     this.autocompleteMode = autocompleteMode; | 
					
						
							|  |  |  |     this.autocompleteSeparator = autocompleteSeparator; | 
					
						
							|  |  |  |     this.menuSelected = 0; | 
					
						
							|  |  |  |     this.current = {}; | 
					
						
							|  |  |  |     this.inputEvent = false; | 
					
						
							|  |  |  |     this.isActive = false; | 
					
						
							|  |  |  |     this.menuContainer = menuContainer; | 
					
						
							|  |  |  |     this.allowSpaces = allowSpaces; | 
					
						
							|  |  |  |     this.replaceTextSuffix = replaceTextSuffix; | 
					
						
							|  |  |  |     this.positionMenu = positionMenu; | 
					
						
							|  |  |  |     this.hasTrailingSpace = false; | 
					
						
							|  |  |  |     this.spaceSelectsMatch = spaceSelectsMatch; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.autocompleteMode) { | 
					
						
							|  |  |  |       trigger = ""; | 
					
						
							|  |  |  |       allowSpaces = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (values) { | 
					
						
							|  |  |  |       this.collection = [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           // symbol that starts the lookup
 | 
					
						
							|  |  |  |           trigger: trigger, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // is it wrapped in an iframe
 | 
					
						
							|  |  |  |           iframe: iframe, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // class applied to selected item
 | 
					
						
							|  |  |  |           selectClass: selectClass, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // class applied to the Container
 | 
					
						
							|  |  |  |           containerClass: containerClass, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // class applied to each item
 | 
					
						
							|  |  |  |           itemClass: itemClass, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // function called on select that retuns the content to insert
 | 
					
						
							|  |  |  |           selectTemplate: ( | 
					
						
							|  |  |  |             selectTemplate || Tribute.defaultSelectTemplate | 
					
						
							|  |  |  |           ).bind(this), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // function called that returns content for an item
 | 
					
						
							|  |  |  |           menuItemTemplate: ( | 
					
						
							|  |  |  |             menuItemTemplate || Tribute.defaultMenuItemTemplate | 
					
						
							|  |  |  |           ).bind(this), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // function called when menu is empty, disables hiding of menu.
 | 
					
						
							|  |  |  |           noMatchTemplate: (t => { | 
					
						
							|  |  |  |             if (typeof t === "string") { | 
					
						
							|  |  |  |               if (t.trim() === "") return null; | 
					
						
							|  |  |  |               return t; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (typeof t === "function") { | 
					
						
							|  |  |  |               return t.bind(this); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return ( | 
					
						
							|  |  |  |               noMatchTemplate || | 
					
						
							|  |  |  |               function() { | 
					
						
							|  |  |  |                 return "<li>No Match Found!</li>"; | 
					
						
							|  |  |  |               }.bind(this) | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           })(noMatchTemplate), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // column to search against in the object
 | 
					
						
							|  |  |  |           lookup: lookup, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // column that contains the content to insert by default
 | 
					
						
							|  |  |  |           fillAttr: fillAttr, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // array of objects or a function returning an array of objects
 | 
					
						
							|  |  |  |           values: values, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // useful for when values is an async function
 | 
					
						
							|  |  |  |           loadingItemTemplate: loadingItemTemplate, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           requireLeadingSpace: requireLeadingSpace, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           searchOpts: searchOpts, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           menuItemLimit: menuItemLimit, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           menuShowMinLength: menuShowMinLength | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ]; | 
					
						
							|  |  |  |     } else if (collection) { | 
					
						
							|  |  |  |       if (this.autocompleteMode) | 
					
						
							|  |  |  |         console.warn( | 
					
						
							|  |  |  |           "Tribute in autocomplete mode does not work for collections" | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       this.collection = collection.map(item => { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           trigger: item.trigger || trigger, | 
					
						
							|  |  |  |           iframe: item.iframe || iframe, | 
					
						
							|  |  |  |           selectClass: item.selectClass || selectClass, | 
					
						
							|  |  |  |           containerClass: item.containerClass || containerClass, | 
					
						
							|  |  |  |           itemClass: item.itemClass || itemClass, | 
					
						
							|  |  |  |           selectTemplate: ( | 
					
						
							|  |  |  |             item.selectTemplate || Tribute.defaultSelectTemplate | 
					
						
							|  |  |  |           ).bind(this), | 
					
						
							|  |  |  |           menuItemTemplate: ( | 
					
						
							|  |  |  |             item.menuItemTemplate || Tribute.defaultMenuItemTemplate | 
					
						
							|  |  |  |           ).bind(this), | 
					
						
							|  |  |  |           // function called when menu is empty, disables hiding of menu.
 | 
					
						
							|  |  |  |           noMatchTemplate: (t => { | 
					
						
							|  |  |  |             if (typeof t === "string") { | 
					
						
							|  |  |  |               if (t.trim() === "") return null; | 
					
						
							|  |  |  |               return t; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (typeof t === "function") { | 
					
						
							|  |  |  |               return t.bind(this); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return ( | 
					
						
							|  |  |  |               noMatchTemplate || | 
					
						
							|  |  |  |               function() { | 
					
						
							|  |  |  |                 return "<li>No Match Found!</li>"; | 
					
						
							|  |  |  |               }.bind(this) | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           })(noMatchTemplate), | 
					
						
							|  |  |  |           lookup: item.lookup || lookup, | 
					
						
							|  |  |  |           fillAttr: item.fillAttr || fillAttr, | 
					
						
							|  |  |  |           values: item.values, | 
					
						
							|  |  |  |           loadingItemTemplate: item.loadingItemTemplate, | 
					
						
							|  |  |  |           requireLeadingSpace: item.requireLeadingSpace, | 
					
						
							|  |  |  |           searchOpts: item.searchOpts || searchOpts, | 
					
						
							|  |  |  |           menuItemLimit: item.menuItemLimit || menuItemLimit, | 
					
						
							|  |  |  |           menuShowMinLength: item.menuShowMinLength || menuShowMinLength | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       throw new Error("[Tribute] No collection specified."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     new TributeRange(this); | 
					
						
							|  |  |  |     new TributeEvents(this); | 
					
						
							|  |  |  |     new TributeMenuEvents(this); | 
					
						
							|  |  |  |     new TributeSearch(this); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   get isActive() { | 
					
						
							|  |  |  |     return this._isActive; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   set isActive(val) { | 
					
						
							|  |  |  |     if (this._isActive != val) { | 
					
						
							|  |  |  |       this._isActive = val; | 
					
						
							|  |  |  |       if (this.current.element) { | 
					
						
							|  |  |  |         let noMatchEvent = new CustomEvent(`tribute-active-${val}`); | 
					
						
							|  |  |  |         this.current.element.dispatchEvent(noMatchEvent); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static defaultSelectTemplate(item) { | 
					
						
							|  |  |  |     if (typeof item === "undefined") | 
					
						
							|  |  |  |       return `${this.current.collection.trigger}${this.current.mentionText}`; | 
					
						
							|  |  |  |     if (this.range.isContentEditable(this.current.element)) { | 
					
						
							|  |  |  |       return ( | 
					
						
							|  |  |  |         '<span class="tribute-mention">' + | 
					
						
							|  |  |  |         (this.current.collection.trigger + | 
					
						
							|  |  |  |           item.original[this.current.collection.fillAttr]) + | 
					
						
							|  |  |  |         "</span>" | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ( | 
					
						
							|  |  |  |       this.current.collection.trigger + | 
					
						
							|  |  |  |       item.original[this.current.collection.fillAttr] | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static defaultMenuItemTemplate(matchItem) { | 
					
						
							|  |  |  |     return matchItem.string; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static inputTypes() { | 
					
						
							|  |  |  |     return ["TEXTAREA", "INPUT"]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   triggers() { | 
					
						
							|  |  |  |     return this.collection.map(config => { | 
					
						
							|  |  |  |       return config.trigger; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   attach(el) { | 
					
						
							|  |  |  |     if (!el) { | 
					
						
							|  |  |  |       throw new Error("[Tribute] Must pass in a DOM node or NodeList."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Check if it is a jQuery collection
 | 
					
						
							|  |  |  |     if (typeof jQuery !== "undefined" && el instanceof jQuery) { | 
					
						
							|  |  |  |       el = el.get(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Is el an Array/Array-like object?
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       el.constructor === NodeList || | 
					
						
							|  |  |  |       el.constructor === HTMLCollection || | 
					
						
							|  |  |  |       el.constructor === Array | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       let length = el.length; | 
					
						
							|  |  |  |       for (var i = 0; i < length; ++i) { | 
					
						
							|  |  |  |         this._attach(el[i]); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       this._attach(el); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _attach(el) { | 
					
						
							|  |  |  |     if (el.hasAttribute("data-tribute")) { | 
					
						
							|  |  |  |       console.warn("Tribute was already bound to " + el.nodeName); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.ensureEditable(el); | 
					
						
							|  |  |  |     this.events.bind(el); | 
					
						
							|  |  |  |     el.setAttribute("data-tribute", true); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ensureEditable(element) { | 
					
						
							|  |  |  |     if (Tribute.inputTypes().indexOf(element.nodeName) === -1) { | 
					
						
							|  |  |  |       if (element.contentEditable) { | 
					
						
							|  |  |  |         element.contentEditable = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         throw new Error("[Tribute] Cannot bind to " + element.nodeName); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   createMenu(containerClass) { | 
					
						
							|  |  |  |     let wrapper = this.range.getDocument().createElement("div"), | 
					
						
							|  |  |  |       ul = this.range.getDocument().createElement("ul"); | 
					
						
							|  |  |  |     wrapper.className = containerClass; | 
					
						
							|  |  |  |     wrapper.appendChild(ul); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.menuContainer) { | 
					
						
							|  |  |  |       return this.menuContainer.appendChild(wrapper); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return this.range.getDocument().body.appendChild(wrapper); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   showMenuFor(element, scrollTo) { | 
					
						
							|  |  |  |     // Only proceed if menu isn't already shown for the current element & mentionText
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       this.isActive && | 
					
						
							|  |  |  |       this.current.element === element && | 
					
						
							|  |  |  |       this.current.mentionText === this.currentMentionTextSnapshot | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this.currentMentionTextSnapshot = this.current.mentionText; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // create the menu if it doesn't exist.
 | 
					
						
							|  |  |  |     if (!this.menu) { | 
					
						
							|  |  |  |       this.menu = this.createMenu(this.current.collection.containerClass); | 
					
						
							|  |  |  |       element.tributeMenu = this.menu; | 
					
						
							|  |  |  |       this.menuEvents.bind(this.menu); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.isActive = true; | 
					
						
							|  |  |  |     this.menuSelected = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!this.current.mentionText) { | 
					
						
							|  |  |  |       this.current.mentionText = ""; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const processValues = values => { | 
					
						
							|  |  |  |       // Tribute may not be active any more by the time the value callback returns
 | 
					
						
							|  |  |  |       if (!this.isActive) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       let items = this.search.filter(this.current.mentionText, values, { | 
					
						
							|  |  |  |         pre: this.current.collection.searchOpts.pre || "<span>", | 
					
						
							|  |  |  |         post: this.current.collection.searchOpts.post || "</span>", | 
					
						
							|  |  |  |         skip: this.current.collection.searchOpts.skip, | 
					
						
							|  |  |  |         extract: el => { | 
					
						
							|  |  |  |           if (typeof this.current.collection.lookup === "string") { | 
					
						
							|  |  |  |             return el[this.current.collection.lookup]; | 
					
						
							|  |  |  |           } else if (typeof this.current.collection.lookup === "function") { | 
					
						
							|  |  |  |             return this.current.collection.lookup(el, this.current.mentionText); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             throw new Error( | 
					
						
							|  |  |  |               "Invalid lookup attribute, lookup must be string or function." | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (this.current.collection.menuItemLimit) { | 
					
						
							|  |  |  |         items = items.slice(0, this.current.collection.menuItemLimit); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this.current.filteredItems = items; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       let ul = this.menu.querySelector("ul"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!items.length) { | 
					
						
							|  |  |  |         let noMatchEvent = new CustomEvent("tribute-no-match", { | 
					
						
							|  |  |  |           detail: this.menu | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         this.current.element.dispatchEvent(noMatchEvent); | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |           (typeof this.current.collection.noMatchTemplate === "function" && | 
					
						
							|  |  |  |             !this.current.collection.noMatchTemplate()) || | 
					
						
							|  |  |  |           !this.current.collection.noMatchTemplate | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |           this.hideMenu(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           typeof this.current.collection.noMatchTemplate === "function" | 
					
						
							|  |  |  |             ? (ul.innerHTML = this.current.collection.noMatchTemplate()) | 
					
						
							|  |  |  |             : (ul.innerHTML = this.current.collection.noMatchTemplate); | 
					
						
							|  |  |  |             this.range.positionMenuAtCaret(scrollTo); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ul.innerHTML = ""; | 
					
						
							|  |  |  |       let fragment = this.range.getDocument().createDocumentFragment(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       items.forEach((item, index) => { | 
					
						
							|  |  |  |         let li = this.range.getDocument().createElement("li"); | 
					
						
							|  |  |  |         li.setAttribute("data-index", index); | 
					
						
							|  |  |  |         li.className = this.current.collection.itemClass; | 
					
						
							|  |  |  |         li.addEventListener("mousemove", e => { | 
					
						
							|  |  |  |           let [li, index] = this._findLiTarget(e.target); | 
					
						
							|  |  |  |           if (e.movementY !== 0) { | 
					
						
							|  |  |  |             this.events.setActiveLi(index); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         if (this.menuSelected === index) { | 
					
						
							|  |  |  |           li.classList.add(this.current.collection.selectClass); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         li.innerHTML = this.current.collection.menuItemTemplate(item); | 
					
						
							|  |  |  |         fragment.appendChild(li); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       ul.appendChild(fragment); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this.range.positionMenuAtCaret(scrollTo); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (typeof this.current.collection.values === "function") { | 
					
						
							|  |  |  |       if (this.current.collection.loadingItemTemplate) { | 
					
						
							|  |  |  |         this.menu.querySelector("ul").innerHTML = this.current.collection.loadingItemTemplate; | 
					
						
							|  |  |  |         this.range.positionMenuAtCaret(scrollTo); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this.current.collection.values(this.current.mentionText, processValues); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       processValues(this.current.collection.values); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _findLiTarget(el) { | 
					
						
							|  |  |  |     if (!el) return []; | 
					
						
							|  |  |  |     const index = el.getAttribute("data-index"); | 
					
						
							|  |  |  |     return !index ? this._findLiTarget(el.parentNode) : [el, index]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   showMenuForCollection(element, collectionIndex) { | 
					
						
							|  |  |  |     if (element !== document.activeElement) { | 
					
						
							|  |  |  |       this.placeCaretAtEnd(element); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.current.collection = this.collection[collectionIndex || 0]; | 
					
						
							|  |  |  |     this.current.externalTrigger = true; | 
					
						
							|  |  |  |     this.current.element = element; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (element.isContentEditable) | 
					
						
							|  |  |  |       this.insertTextAtCursor(this.current.collection.trigger); | 
					
						
							|  |  |  |     else this.insertAtCaret(element, this.current.collection.trigger); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.showMenuFor(element); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO: make sure this works for inputs/textareas
 | 
					
						
							|  |  |  |   placeCaretAtEnd(el) { | 
					
						
							|  |  |  |     el.focus(); | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       typeof window.getSelection != "undefined" && | 
					
						
							|  |  |  |       typeof document.createRange != "undefined" | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       var range = document.createRange(); | 
					
						
							|  |  |  |       range.selectNodeContents(el); | 
					
						
							|  |  |  |       range.collapse(false); | 
					
						
							|  |  |  |       var sel = window.getSelection(); | 
					
						
							|  |  |  |       sel.removeAllRanges(); | 
					
						
							|  |  |  |       sel.addRange(range); | 
					
						
							|  |  |  |     } else if (typeof document.body.createTextRange != "undefined") { | 
					
						
							|  |  |  |       var textRange = document.body.createTextRange(); | 
					
						
							|  |  |  |       textRange.moveToElementText(el); | 
					
						
							|  |  |  |       textRange.collapse(false); | 
					
						
							|  |  |  |       textRange.select(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // for contenteditable
 | 
					
						
							|  |  |  |   insertTextAtCursor(text) { | 
					
						
							|  |  |  |     var sel, range; | 
					
						
							|  |  |  |     sel = window.getSelection(); | 
					
						
							|  |  |  |     range = sel.getRangeAt(0); | 
					
						
							|  |  |  |     range.deleteContents(); | 
					
						
							|  |  |  |     var textNode = document.createTextNode(text); | 
					
						
							|  |  |  |     range.insertNode(textNode); | 
					
						
							|  |  |  |     range.selectNodeContents(textNode); | 
					
						
							|  |  |  |     range.collapse(false); | 
					
						
							|  |  |  |     sel.removeAllRanges(); | 
					
						
							|  |  |  |     sel.addRange(range); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // for regular inputs
 | 
					
						
							|  |  |  |   insertAtCaret(textarea, text) { | 
					
						
							|  |  |  |     var scrollPos = textarea.scrollTop; | 
					
						
							|  |  |  |     var caretPos = textarea.selectionStart; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var front = textarea.value.substring(0, caretPos); | 
					
						
							|  |  |  |     var back = textarea.value.substring( | 
					
						
							|  |  |  |       textarea.selectionEnd, | 
					
						
							|  |  |  |       textarea.value.length | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     textarea.value = front + text + back; | 
					
						
							|  |  |  |     caretPos = caretPos + text.length; | 
					
						
							|  |  |  |     textarea.selectionStart = caretPos; | 
					
						
							|  |  |  |     textarea.selectionEnd = caretPos; | 
					
						
							|  |  |  |     textarea.focus(); | 
					
						
							|  |  |  |     textarea.scrollTop = scrollPos; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   hideMenu() { | 
					
						
							|  |  |  |     if (this.menu) { | 
					
						
							|  |  |  |       this.menu.style.cssText = "display: none;"; | 
					
						
							|  |  |  |       this.isActive = false; | 
					
						
							|  |  |  |       this.menuSelected = 0; | 
					
						
							|  |  |  |       this.current = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   selectItemAtIndex(index, originalEvent) { | 
					
						
							|  |  |  |     index = parseInt(index); | 
					
						
							|  |  |  |     if (typeof index !== "number" || isNaN(index)) return; | 
					
						
							|  |  |  |     let item = this.current.filteredItems[index]; | 
					
						
							|  |  |  |     let content = this.current.collection.selectTemplate(item); | 
					
						
							|  |  |  |     if (content !== null) this.replaceText(content, originalEvent, item); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   replaceText(content, originalEvent, item) { | 
					
						
							|  |  |  |     this.range.replaceTriggerText(content, true, true, originalEvent, item); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _append(collection, newValues, replace) { | 
					
						
							|  |  |  |     if (typeof collection.values === "function") { | 
					
						
							|  |  |  |       throw new Error("Unable to append to values, as it is a function."); | 
					
						
							|  |  |  |     } else if (!replace) { | 
					
						
							|  |  |  |       collection.values = collection.values.concat(newValues); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       collection.values = newValues; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   append(collectionIndex, newValues, replace) { | 
					
						
							|  |  |  |     let index = parseInt(collectionIndex); | 
					
						
							|  |  |  |     if (typeof index !== "number") | 
					
						
							|  |  |  |       throw new Error("please provide an index for the collection to update."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let collection = this.collection[index]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._append(collection, newValues, replace); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   appendCurrent(newValues, replace) { | 
					
						
							|  |  |  |     if (this.isActive) { | 
					
						
							|  |  |  |       this._append(this.current.collection, newValues, replace); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       throw new Error( | 
					
						
							|  |  |  |         "No active state. Please use append instead and pass an index." | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   detach(el) { | 
					
						
							|  |  |  |     if (!el) { | 
					
						
							|  |  |  |       throw new Error("[Tribute] Must pass in a DOM node or NodeList."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Check if it is a jQuery collection
 | 
					
						
							|  |  |  |     if (typeof jQuery !== "undefined" && el instanceof jQuery) { | 
					
						
							|  |  |  |       el = el.get(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Is el an Array/Array-like object?
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       el.constructor === NodeList || | 
					
						
							|  |  |  |       el.constructor === HTMLCollection || | 
					
						
							|  |  |  |       el.constructor === Array | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       let length = el.length; | 
					
						
							|  |  |  |       for (var i = 0; i < length; ++i) { | 
					
						
							|  |  |  |         this._detach(el[i]); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       this._detach(el); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _detach(el) { | 
					
						
							|  |  |  |     this.events.unbind(el); | 
					
						
							|  |  |  |     if (el.tributeMenu) { | 
					
						
							|  |  |  |       this.menuEvents.unbind(el.tributeMenu); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setTimeout(() => { | 
					
						
							|  |  |  |       el.removeAttribute("data-tribute"); | 
					
						
							|  |  |  |       this.isActive = false; | 
					
						
							|  |  |  |       if (el.tributeMenu) { | 
					
						
							|  |  |  |         el.tributeMenu.remove(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Tribute.js | 
					
						
							|  |  |  |  * Native ES6 JavaScript @mention Plugin | 
					
						
							|  |  |  |  **/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default Tribute; |