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