1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-05-10 17:45:18 +02:00
php-phpbb/phpBB/assets/javascript/jquery.tribute.js
2021-05-14 21:52:16 +02:00

1899 lines
66 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Tribute = factory());
}(this, (function () { 'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
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") {
var CustomEvent$1 = function CustomEvent(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;
}
var TributeEvents = /*#__PURE__*/function () {
function TributeEvents(tribute) {
_classCallCheck(this, TributeEvents);
this.tribute = tribute;
this.tribute.events = this;
}
_createClass(TributeEvents, [{
key: "bind",
value: function 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);
}
}, {
key: "unbind",
value: function 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;
}
}, {
key: "keydown",
value: function keydown(instance, event) {
if (instance.shouldDeactivate(event)) {
instance.tribute.isActive = false;
instance.tribute.hideMenu();
}
var element = this;
instance.commandEvent = false;
TributeEvents.keys().forEach(function (o) {
if (o.key === event.keyCode) {
instance.commandEvent = true;
instance.callbacks()[o.value.toLowerCase()](event, element);
}
});
}
}, {
key: "input",
value: function input(instance, event) {
instance.inputEvent = true;
instance.keyup.call(this, instance, event);
}
}, {
key: "click",
value: function click(instance, event) {
var tribute = instance.tribute;
if (tribute.menu && tribute.menu.contains(event.target)) {
var 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(function () {
return tribute.hideMenu();
});
}
}
}, {
key: "keyup",
value: function 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 {
var keyCode = instance.getKeyCode(instance, this, event);
if (isNaN(keyCode) || !keyCode) return;
var trigger = instance.tribute.triggers().find(function (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);
}
}
}, {
key: "shouldDeactivate",
value: function shouldDeactivate(event) {
if (!this.tribute.isActive) return false;
if (this.tribute.current.mentionText.length === 0) {
var eventKeyPressed = false;
TributeEvents.keys().forEach(function (o) {
if (event.keyCode === o.key) eventKeyPressed = true;
});
return !eventKeyPressed;
}
return false;
}
}, {
key: "getKeyCode",
value: function getKeyCode(instance, el, event) {
var tribute = instance.tribute;
var info = tribute.range.getTriggerInfo(false, tribute.hasTrailingSpace, true, tribute.allowSpaces, tribute.autocompleteMode);
if (info) {
return info.mentionTriggerChar.charCodeAt(0);
} else {
return false;
}
}
}, {
key: "updateSelection",
value: function updateSelection(el) {
this.tribute.current.element = el;
var 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;
}
}
}, {
key: "callbacks",
value: function callbacks() {
var _this = this;
return {
triggerChar: function triggerChar(e, el, trigger) {
var tribute = _this.tribute;
tribute.current.trigger = trigger;
var collectionItem = tribute.collection.find(function (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: function enter(e, el) {
// choose selection
if (_this.tribute.isActive && _this.tribute.current.filteredItems) {
e.preventDefault();
e.stopPropagation();
setTimeout(function () {
_this.tribute.selectItemAtIndex(_this.tribute.menuSelected, e);
_this.tribute.hideMenu();
}, 0);
}
},
escape: function escape(e, el) {
if (_this.tribute.isActive) {
e.preventDefault();
e.stopPropagation();
_this.tribute.isActive = false;
_this.tribute.hideMenu();
}
},
tab: function tab(e, el) {
// choose first match
_this.callbacks().enter(e, el);
},
space: function space(e, el) {
if (_this.tribute.isActive) {
if (_this.tribute.spaceSelectsMatch) {
_this.callbacks().enter(e, el);
} else if (!_this.tribute.allowSpaces) {
e.stopPropagation();
setTimeout(function () {
_this.tribute.hideMenu();
_this.tribute.isActive = false;
}, 0);
}
}
},
up: function up(e, el) {
// navigate up ul
if (_this.tribute.isActive && _this.tribute.current.filteredItems) {
e.preventDefault();
e.stopPropagation();
var 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: function down(e, el) {
// navigate down ul
if (_this.tribute.isActive && _this.tribute.current.filteredItems) {
e.preventDefault();
e.stopPropagation();
var 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": function _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);
}
}
};
}
}, {
key: "setActiveLi",
value: function setActiveLi(index) {
var lis = this.tribute.menu.querySelectorAll("li"),
length = lis.length >>> 0;
if (index) this.tribute.menuSelected = parseInt(index);
for (var i = 0; i < length; i++) {
var li = lis[i];
if (i === this.tribute.menuSelected) {
li.classList.add(this.tribute.current.collection.selectClass);
var liClientRect = li.getBoundingClientRect();
var menuClientRect = this.tribute.menu.getBoundingClientRect();
if (liClientRect.bottom > menuClientRect.bottom) {
var scrollDistance = liClientRect.bottom - menuClientRect.bottom;
this.tribute.menu.scrollTop += scrollDistance;
} else if (liClientRect.top < menuClientRect.top) {
var _scrollDistance = menuClientRect.top - liClientRect.top;
this.tribute.menu.scrollTop -= _scrollDistance;
}
} else {
li.classList.remove(this.tribute.current.collection.selectClass);
}
}
}
}, {
key: "getFullHeight",
value: function getFullHeight(elem, includeMargin) {
var height = elem.getBoundingClientRect().height;
if (includeMargin) {
var style = elem.currentStyle || window.getComputedStyle(elem);
return height + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
}
return height;
}
}], [{
key: "keys",
value: function 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"
}];
}
}]);
return TributeEvents;
}();
var TributeMenuEvents = /*#__PURE__*/function () {
function TributeMenuEvents(tribute) {
_classCallCheck(this, TributeMenuEvents);
this.tribute = tribute;
this.tribute.menuEvents = this;
this.menu = this.tribute.menu;
}
_createClass(TributeMenuEvents, [{
key: "bind",
value: function bind(menu) {
var _this = this;
this.menuClickEvent = this.tribute.events.click.bind(null, this);
this.menuContainerScrollEvent = this.debounce(function () {
if (_this.tribute.isActive) {
_this.tribute.showMenuFor(_this.tribute.current.element, false);
}
}, 300, false);
this.windowResizeEvent = this.debounce(function () {
if (_this.tribute.isActive) {
_this.tribute.range.positionMenuAtCaret(true);
}
}, 300, 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);
}
}
}, {
key: "unbind",
value: function 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);
}
}
}, {
key: "debounce",
value: function debounce(func, wait, immediate) {
var _arguments = arguments,
_this2 = this;
var timeout;
return function () {
var context = _this2,
args = _arguments;
var later = function 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);
};
}
}]);
return TributeMenuEvents;
}();
var TributeRange = /*#__PURE__*/function () {
function TributeRange(tribute) {
_classCallCheck(this, TributeRange);
this.tribute = tribute;
this.tribute.range = this;
}
_createClass(TributeRange, [{
key: "getDocument",
value: function getDocument() {
var iframe;
if (this.tribute.current.collection) {
iframe = this.tribute.current.collection.iframe;
}
if (!iframe) {
return document;
}
return iframe.contentWindow.document;
}
}, {
key: "positionMenuAtCaret",
value: function positionMenuAtCaret(scrollTo) {
var _this = this;
var context = this.tribute.current,
coordinates;
var 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: ".concat(coordinates.top, "px;\n left: ").concat(coordinates.left, "px;\n right: ").concat(coordinates.right, "px;\n bottom: ").concat(coordinates.bottom, "px;\n position: absolute;\n 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();
window.setTimeout(function () {
var menuDimensions = {
width: _this.tribute.menu.offsetWidth,
height: _this.tribute.menu.offsetHeight
};
var menuIsOffScreen = _this.isMenuOffScreen(coordinates, menuDimensions);
var menuIsOffScreenHorizontally = window.innerWidth > menuDimensions.width && (menuIsOffScreen.left || menuIsOffScreen.right);
var menuIsOffScreenVertically = window.innerHeight > menuDimensions.height && (menuIsOffScreen.top || menuIsOffScreen.bottom);
if (menuIsOffScreenHorizontally || menuIsOffScreenVertically) {
_this.tribute.menu.style.cssText = 'display: none';
_this.positionMenuAtCaret(scrollTo);
}
}, 0);
} else {
this.tribute.menu.style.cssText = 'display: none';
}
}
}, {
key: "selectElement",
value: function selectElement(targetElement, path, offset) {
var range;
var 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;
}
}
}
var 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();
}
}, {
key: "replaceTriggerText",
value: function replaceTriggerText(text, requireLeadingSpace, hasTrailingSpace, originalEvent, item) {
var info = this.getTriggerInfo(true, hasTrailingSpace, requireLeadingSpace, this.tribute.allowSpaces, this.tribute.autocompleteMode);
if (info !== undefined) {
var context = this.tribute.current;
var replaceEvent = new CustomEvent('tribute-replaced', {
detail: {
item: item,
instance: context,
context: info,
event: originalEvent
}
});
if (!this.isContentEditable(context.element)) {
var myField = this.tribute.current.element;
var textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : ' ';
text += textSuffix;
var startPos = info.mentionPosition;
var 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
var _textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : '\xA0';
text += _textSuffix;
var _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);
}
}
}, {
key: "pasteHtml",
value: function pasteHtml(html, startPos, endPos) {
var range, sel;
sel = this.getWindowSelection();
range = this.getDocument().createRange();
range.setStart(sel.anchorNode, startPos);
range.setEnd(sel.anchorNode, endPos);
range.deleteContents();
var el = this.getDocument().createElement('div');
el.innerHTML = html;
var 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);
}
}
}, {
key: "getWindowSelection",
value: function getWindowSelection() {
if (this.tribute.collection.iframe) {
return this.tribute.collection.iframe.contentWindow.getSelection();
}
return window.getSelection();
}
}, {
key: "getNodePositionInParent",
value: function getNodePositionInParent(element) {
if (element.parentNode === null) {
return 0;
}
for (var i = 0; i < element.parentNode.childNodes.length; i++) {
var node = element.parentNode.childNodes[i];
if (node === element) {
return i;
}
}
}
}, {
key: "getContentEditableSelectedPath",
value: function getContentEditableSelectedPath(ctx) {
var sel = this.getWindowSelection();
var selected = sel.anchorNode;
var path = [];
var offset;
if (selected != null) {
var i;
var 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
};
}
}
}, {
key: "getTextPrecedingCurrentSelection",
value: function getTextPrecedingCurrentSelection() {
var context = this.tribute.current,
text = '';
if (!this.isContentEditable(context.element)) {
var textComponent = this.tribute.current.element;
if (textComponent) {
var startPos = textComponent.selectionStart;
if (textComponent.value && startPos >= 0) {
text = textComponent.value.substring(0, startPos);
}
}
} else {
var selectedElem = this.getWindowSelection().anchorNode;
if (selectedElem != null) {
var workingNodeContent = selectedElem.textContent;
var selectStartOffset = this.getWindowSelection().getRangeAt(0).startOffset;
if (workingNodeContent && selectStartOffset >= 0) {
text = workingNodeContent.substring(0, selectStartOffset);
}
}
}
return text;
}
}, {
key: "getLastWordInText",
value: function 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();
}
}, {
key: "getTriggerInfo",
value: function getTriggerInfo(menuAlreadyActive, hasTrailingSpace, requireLeadingSpace, allowSpaces, isAutocomplete) {
var _this2 = this;
var ctx = this.tribute.current;
var selected, path, offset;
if (!this.isContentEditable(ctx.element)) {
selected = this.tribute.current.element;
} else {
var selectionInfo = this.getContentEditableSelectedPath(ctx);
if (selectionInfo) {
selected = selectionInfo.selected;
path = selectionInfo.path;
offset = selectionInfo.offset;
}
}
var effectiveRange = this.getTextPrecedingCurrentSelection();
var 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) {
var mostRecentTriggerCharPos = -1;
var triggerChar;
this.tribute.collection.forEach(function (config) {
var c = config.trigger;
var idx = config.requireLeadingSpace ? _this2.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)))) {
var currentTriggerSnippet = effectiveRange.substring(mostRecentTriggerCharPos + triggerChar.length, effectiveRange.length);
triggerChar = effectiveRange.substring(mostRecentTriggerCharPos, mostRecentTriggerCharPos + triggerChar.length);
var firstSnippetChar = currentTriggerSnippet.substring(0, 1);
var leadingSpace = currentTriggerSnippet.length > 0 && (firstSnippetChar === ' ' || firstSnippetChar === '\xA0');
if (hasTrailingSpace) {
currentTriggerSnippet = currentTriggerSnippet.trim();
}
var 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
};
}
}
}
}
}, {
key: "lastIndexWithLeadingSpace",
value: function lastIndexWithLeadingSpace(str, trigger) {
var reversedStr = str.split('').reverse().join('');
var index = -1;
for (var cidx = 0, len = str.length; cidx < len; cidx++) {
var firstChar = cidx === str.length - 1;
var leadingSpace = /\s/.test(reversedStr[cidx + 1]);
var match = true;
for (var 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;
}
}, {
key: "isContentEditable",
value: function isContentEditable(element) {
return element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA';
}
}, {
key: "isMenuOffScreen",
value: function isMenuOffScreen(coordinates, menuDimensions) {
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var doc = document.documentElement;
var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
var menuTop = typeof coordinates.top === 'number' ? coordinates.top : windowTop + windowHeight - coordinates.bottom - menuDimensions.height;
var menuRight = typeof coordinates.right === 'number' ? coordinates.right : coordinates.left + menuDimensions.width;
var menuBottom = typeof coordinates.bottom === 'number' ? coordinates.bottom : coordinates.top + menuDimensions.height;
var 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)
};
}
}, {
key: "getMenuDimensions",
value: function 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
var dimensions = {
width: null,
height: null
};
this.tribute.menu.style.cssText = "top: 0px;\n left: 0px;\n position: fixed;\n display: block;\n visibility; hidden;";
dimensions.width = this.tribute.menu.offsetWidth;
dimensions.height = this.tribute.menu.offsetHeight;
this.tribute.menu.style.cssText = "display: none;";
return dimensions;
}
}, {
key: "getTextAreaOrInputUnderlinePosition",
value: function getTextAreaOrInputUnderlinePosition(element, position, flipped) {
var properties = ['direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing'];
var isFirefox = window.mozInnerScreenX !== null;
var div = this.getDocument().createElement('div');
div.id = 'input-textarea-caret-position-mirror-div';
this.getDocument().body.appendChild(div);
var style = div.style;
var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle;
style.whiteSpace = 'pre-wrap';
if (element.nodeName !== 'INPUT') {
style.wordWrap = 'break-word';
} // position off-screen
style.position = 'absolute';
style.visibility = 'hidden'; // transfer the element's properties to the div
properties.forEach(function (prop) {
style[prop] = computed[prop];
});
if (isFirefox) {
style.width = "".concat(parseInt(computed.width) - 2, "px");
if (element.scrollHeight > parseInt(computed.height)) style.overflowY = 'scroll';
} else {
style.overflow = 'hidden';
}
div.textContent = element.value.substring(0, position);
if (element.nodeName === 'INPUT') {
div.textContent = div.textContent.replace(/\s/g, ' ');
}
var span = this.getDocument().createElement('span');
span.textContent = element.value.substring(position) || '.';
div.appendChild(span);
var rect = element.getBoundingClientRect();
var doc = document.documentElement;
var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
var top = 0;
var left = 0;
if (this.menuContainerIsBody) {
top = rect.top;
left = rect.left;
}
var coordinates = {
top: top + windowTop + span.offsetTop + parseInt(computed.borderTopWidth) + parseInt(computed.fontSize) - element.scrollTop,
left: left + windowLeft + span.offsetLeft + parseInt(computed.borderLeftWidth)
};
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var menuDimensions = this.getMenuDimensions();
var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
if (menuIsOffScreen.right) {
coordinates.right = windowWidth - coordinates.left;
coordinates.left = 'auto';
}
var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight;
if (menuIsOffScreen.bottom) {
var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect();
var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top);
coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top - span.offsetTop);
coordinates.top = 'auto';
}
menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
if (menuIsOffScreen.left) {
coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft;
delete coordinates.right;
}
if (menuIsOffScreen.top) {
coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop;
delete coordinates.bottom;
}
this.getDocument().body.removeChild(div);
return coordinates;
}
}, {
key: "getContentEditableCaretPosition",
value: function getContentEditableCaretPosition(selectedNodePosition) {
var range;
var sel = this.getWindowSelection();
range = this.getDocument().createRange();
range.setStart(sel.anchorNode, selectedNodePosition);
range.setEnd(sel.anchorNode, selectedNodePosition);
range.collapse(false);
var rect = range.getBoundingClientRect();
var doc = document.documentElement;
var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
var left = rect.left;
var top = rect.top;
var coordinates = {
left: left + windowLeft,
top: top + rect.height + windowTop
};
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var menuDimensions = this.getMenuDimensions();
var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
if (menuIsOffScreen.right) {
coordinates.left = 'auto';
coordinates.right = windowWidth - rect.left - windowLeft;
}
var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight;
if (menuIsOffScreen.bottom) {
var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect();
var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top);
coordinates.top = 'auto';
coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top);
}
menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions);
if (menuIsOffScreen.left) {
coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft;
delete coordinates.right;
}
if (menuIsOffScreen.top) {
coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop;
delete coordinates.bottom;
}
if (!this.menuContainerIsBody) {
coordinates.left = coordinates.left ? coordinates.left - this.tribute.menuContainer.offsetLeft : coordinates.left;
coordinates.top = coordinates.top ? coordinates.top - this.tribute.menuContainer.offsetTop : coordinates.top;
}
return coordinates;
}
}, {
key: "scrollIntoView",
value: function scrollIntoView(elem) {
var reasonableBuffer = 20,
clientRect;
var maxScrollDisplacement = 100;
var 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;
}
}
}
var elemTop = clientRect.top;
var elemBottom = elemTop + clientRect.height;
if (elemTop < 0) {
window.scrollTo(0, window.pageYOffset + clientRect.top - reasonableBuffer);
} else if (elemBottom > window.innerHeight) {
var maxY = window.pageYOffset + clientRect.top - reasonableBuffer;
if (maxY - window.pageYOffset > maxScrollDisplacement) {
maxY = window.pageYOffset + maxScrollDisplacement;
}
var targetY = window.pageYOffset - (window.innerHeight - elemBottom);
if (targetY > maxY) {
targetY = maxY;
}
window.scrollTo(0, targetY);
}
}
}, {
key: "menuContainerIsBody",
get: function get() {
return this.tribute.menuContainer === document.body || !this.tribute.menuContainer;
}
}]);
return TributeRange;
}();
// Thanks to https://github.com/mattyork/fuzzy
var TributeSearch = /*#__PURE__*/function () {
function TributeSearch(tribute) {
_classCallCheck(this, TributeSearch);
this.tribute = tribute;
this.tribute.search = this;
}
_createClass(TributeSearch, [{
key: "simpleFilter",
value: function simpleFilter(pattern, array) {
var _this = this;
return array.filter(function (string) {
return _this.test(pattern, string);
});
}
}, {
key: "test",
value: function test(pattern, string) {
return this.match(pattern, string) !== null;
}
}, {
key: "match",
value: function match(pattern, string, opts) {
opts = opts || {};
var 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();
var patternCache = this.traverse(compareString, pattern, 0, 0, []);
if (!patternCache) {
return null;
}
return {
rendered: this.render(string, patternCache.cache, pre, post),
score: patternCache.score
};
}
}, {
key: "traverse",
value: function 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;
}
var c = pattern[patternIndex];
var index = string.indexOf(c, stringIndex);
var 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;
}
}, {
key: "calculateScore",
value: function calculateScore(patternCache) {
var score = 0;
var temp = 1;
patternCache.forEach(function (index, i) {
if (i > 0) {
if (patternCache[i - 1] + 1 === index) {
temp += temp + 1;
} else {
temp = 1;
}
}
score += temp;
});
return score;
}
}, {
key: "render",
value: function render(string, indices, pre, post) {
var rendered = string.substring(0, indices[0]);
indices.forEach(function (index, i) {
rendered += pre + string[index] + post + string.substring(index + 1, indices[i + 1] ? indices[i + 1] : string.length);
});
return rendered;
}
}, {
key: "filter",
value: function filter(pattern, arr, opts) {
var _this2 = this;
opts = opts || {};
return arr.reduce(function (prev, element, idx, arr) {
var str = element;
if (opts.extract) {
str = opts.extract(element);
if (!str) {
// take care of undefineds / nulls / etc.
str = '';
}
}
var rendered = _this2.match(pattern, str, opts);
if (rendered != null) {
prev[prev.length] = {
string: rendered.rendered,
score: rendered.score,
index: idx,
original: element
};
}
return prev;
}, []).sort(function (a, b) {
var compare = b.score - a.score;
if (compare) return compare;
return a.index - b.index;
});
}
}]);
return TributeSearch;
}();
var Tribute = /*#__PURE__*/function () {
function Tribute(_ref) {
var _this = this;
var _ref$values = _ref.values,
values = _ref$values === void 0 ? null : _ref$values,
_ref$loadingItemTempl = _ref.loadingItemTemplate,
loadingItemTemplate = _ref$loadingItemTempl === void 0 ? null : _ref$loadingItemTempl,
_ref$iframe = _ref.iframe,
iframe = _ref$iframe === void 0 ? null : _ref$iframe,
_ref$selectClass = _ref.selectClass,
selectClass = _ref$selectClass === void 0 ? "highlight" : _ref$selectClass,
_ref$containerClass = _ref.containerClass,
containerClass = _ref$containerClass === void 0 ? "tribute-container" : _ref$containerClass,
_ref$itemClass = _ref.itemClass,
itemClass = _ref$itemClass === void 0 ? "" : _ref$itemClass,
_ref$trigger = _ref.trigger,
trigger = _ref$trigger === void 0 ? "@" : _ref$trigger,
_ref$autocompleteMode = _ref.autocompleteMode,
autocompleteMode = _ref$autocompleteMode === void 0 ? false : _ref$autocompleteMode,
_ref$autocompleteSepa = _ref.autocompleteSeparator,
autocompleteSeparator = _ref$autocompleteSepa === void 0 ? null : _ref$autocompleteSepa,
_ref$selectTemplate = _ref.selectTemplate,
selectTemplate = _ref$selectTemplate === void 0 ? null : _ref$selectTemplate,
_ref$menuItemTemplate = _ref.menuItemTemplate,
menuItemTemplate = _ref$menuItemTemplate === void 0 ? null : _ref$menuItemTemplate,
_ref$lookup = _ref.lookup,
lookup = _ref$lookup === void 0 ? "key" : _ref$lookup,
_ref$fillAttr = _ref.fillAttr,
fillAttr = _ref$fillAttr === void 0 ? "value" : _ref$fillAttr,
_ref$collection = _ref.collection,
collection = _ref$collection === void 0 ? null : _ref$collection,
_ref$menuContainer = _ref.menuContainer,
menuContainer = _ref$menuContainer === void 0 ? null : _ref$menuContainer,
_ref$noMatchTemplate = _ref.noMatchTemplate,
noMatchTemplate = _ref$noMatchTemplate === void 0 ? null : _ref$noMatchTemplate,
_ref$requireLeadingSp = _ref.requireLeadingSpace,
requireLeadingSpace = _ref$requireLeadingSp === void 0 ? true : _ref$requireLeadingSp,
_ref$allowSpaces = _ref.allowSpaces,
allowSpaces = _ref$allowSpaces === void 0 ? false : _ref$allowSpaces,
_ref$replaceTextSuffi = _ref.replaceTextSuffix,
replaceTextSuffix = _ref$replaceTextSuffi === void 0 ? null : _ref$replaceTextSuffi,
_ref$positionMenu = _ref.positionMenu,
positionMenu = _ref$positionMenu === void 0 ? true : _ref$positionMenu,
_ref$spaceSelectsMatc = _ref.spaceSelectsMatch,
spaceSelectsMatch = _ref$spaceSelectsMatc === void 0 ? false : _ref$spaceSelectsMatc,
_ref$searchOpts = _ref.searchOpts,
searchOpts = _ref$searchOpts === void 0 ? {} : _ref$searchOpts,
_ref$menuItemLimit = _ref.menuItemLimit,
menuItemLimit = _ref$menuItemLimit === void 0 ? null : _ref$menuItemLimit,
_ref$menuShowMinLengt = _ref.menuShowMinLength,
menuShowMinLength = _ref$menuShowMinLengt === void 0 ? 0 : _ref$menuShowMinLengt;
_classCallCheck(this, Tribute);
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: function (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(function (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: function (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);
}
_createClass(Tribute, [{
key: "triggers",
value: function triggers() {
return this.collection.map(function (config) {
return config.trigger;
});
}
}, {
key: "attach",
value: function 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) {
var length = el.length;
for (var i = 0; i < length; ++i) {
this._attach(el[i]);
}
} else {
this._attach(el);
}
}
}, {
key: "_attach",
value: function _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);
}
}, {
key: "ensureEditable",
value: function 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);
}
}
}
}, {
key: "createMenu",
value: function createMenu(containerClass) {
var 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);
}
}, {
key: "showMenuFor",
value: function showMenuFor(element, scrollTo) {
var _this2 = this;
// 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 = "";
}
var processValues = function processValues(values) {
// Tribute may not be active any more by the time the value callback returns
if (!_this2.isActive) {
return;
}
var items = _this2.search.filter(_this2.current.mentionText, values, {
pre: _this2.current.collection.searchOpts.pre || "<span>",
post: _this2.current.collection.searchOpts.post || "</span>",
skip: _this2.current.collection.searchOpts.skip,
extract: function extract(el) {
if (typeof _this2.current.collection.lookup === "string") {
return el[_this2.current.collection.lookup];
} else if (typeof _this2.current.collection.lookup === "function") {
return _this2.current.collection.lookup(el, _this2.current.mentionText);
} else {
throw new Error("Invalid lookup attribute, lookup must be string or function.");
}
}
});
if (_this2.current.collection.menuItemLimit) {
items = items.slice(0, _this2.current.collection.menuItemLimit);
}
_this2.current.filteredItems = items;
var ul = _this2.menu.querySelector("ul");
_this2.range.positionMenuAtCaret(scrollTo);
if (!items.length) {
var noMatchEvent = new CustomEvent("tribute-no-match", {
detail: _this2.menu
});
_this2.current.element.dispatchEvent(noMatchEvent);
if (typeof _this2.current.collection.noMatchTemplate === "function" && !_this2.current.collection.noMatchTemplate() || !_this2.current.collection.noMatchTemplate) {
_this2.hideMenu();
} else {
typeof _this2.current.collection.noMatchTemplate === "function" ? ul.innerHTML = _this2.current.collection.noMatchTemplate() : ul.innerHTML = _this2.current.collection.noMatchTemplate;
}
return;
}
ul.innerHTML = "";
var fragment = _this2.range.getDocument().createDocumentFragment();
items.forEach(function (item, index) {
var li = _this2.range.getDocument().createElement("li");
li.setAttribute("data-index", index);
li.className = _this2.current.collection.itemClass;
li.addEventListener("mousemove", function (e) {
var _this2$_findLiTarget = _this2._findLiTarget(e.target),
_this2$_findLiTarget2 = _slicedToArray(_this2$_findLiTarget, 2),
li = _this2$_findLiTarget2[0],
index = _this2$_findLiTarget2[1];
if (e.movementY !== 0) {
_this2.events.setActiveLi(index);
}
});
if (_this2.menuSelected === index) {
li.classList.add(_this2.current.collection.selectClass);
}
li.innerHTML = _this2.current.collection.menuItemTemplate(item);
fragment.appendChild(li);
});
ul.appendChild(fragment);
};
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);
}
}
}, {
key: "_findLiTarget",
value: function _findLiTarget(el) {
if (!el) return [];
var index = el.getAttribute("data-index");
return !index ? this._findLiTarget(el.parentNode) : [el, index];
}
}, {
key: "showMenuForCollection",
value: function 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
}, {
key: "placeCaretAtEnd",
value: function 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
}, {
key: "insertTextAtCursor",
value: function 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
}, {
key: "insertAtCaret",
value: function 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;
}
}, {
key: "hideMenu",
value: function hideMenu() {
if (this.menu) {
this.menu.style.cssText = "display: none;";
this.isActive = false;
this.menuSelected = 0;
this.current = {};
}
}
}, {
key: "selectItemAtIndex",
value: function selectItemAtIndex(index, originalEvent) {
index = parseInt(index);
if (typeof index !== "number" || isNaN(index)) return;
var item = this.current.filteredItems[index];
var content = this.current.collection.selectTemplate(item);
if (content !== null) this.replaceText(content, originalEvent, item);
}
}, {
key: "replaceText",
value: function replaceText(content, originalEvent, item) {
this.range.replaceTriggerText(content, true, true, originalEvent, item);
}
}, {
key: "_append",
value: function _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;
}
}
}, {
key: "append",
value: function append(collectionIndex, newValues, replace) {
var index = parseInt(collectionIndex);
if (typeof index !== "number") throw new Error("please provide an index for the collection to update.");
var collection = this.collection[index];
this._append(collection, newValues, replace);
}
}, {
key: "appendCurrent",
value: function 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.");
}
}
}, {
key: "detach",
value: function 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) {
var length = el.length;
for (var i = 0; i < length; ++i) {
this._detach(el[i]);
}
} else {
this._detach(el);
}
}
}, {
key: "_detach",
value: function _detach(el) {
var _this3 = this;
this.events.unbind(el);
if (el.tributeMenu) {
this.menuEvents.unbind(el.tributeMenu);
}
setTimeout(function () {
el.removeAttribute("data-tribute");
_this3.isActive = false;
if (el.tributeMenu) {
el.tributeMenu.remove();
}
});
}
}, {
key: "isActive",
get: function get() {
return this._isActive;
},
set: function set(val) {
if (this._isActive != val) {
this._isActive = val;
if (this.current.element) {
var noMatchEvent = new CustomEvent("tribute-active-".concat(val));
this.current.element.dispatchEvent(noMatchEvent);
}
}
}
}], [{
key: "defaultSelectTemplate",
value: function defaultSelectTemplate(item) {
if (typeof item === "undefined") return "".concat(this.current.collection.trigger).concat(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];
}
}, {
key: "defaultMenuItemTemplate",
value: function defaultMenuItemTemplate(matchItem) {
return matchItem.string;
}
}, {
key: "inputTypes",
value: function inputTypes() {
return ["TEXTAREA", "INPUT"];
}
}]);
return Tribute;
}();
/**
* Tribute.js
* Native ES6 JavaScript @mention Plugin
**/
return Tribute;
})));