1//= link ./stimulus-autoloader.js 2//= link ./stimulus-importmap-autoloader.js 3//= link ./stimulus-loading.js 4 5/* 6Stimulus 3.2.2
vendor: 88,663 bytes, lines 6-2567
6 7Copyright © 2023 Basecamp, LLC 8 */ 9class EventListener { 10 constructor(eventTarget, eventName, eventOptions) { 11 this.eventTarget = eventTarget; 12 this.eventName = eventName; 13 this.eventOptions = eventOptions; 14 this.unorderedBindings = new Set(); 15 } 16 connect() { 17 this.eventTarget.addEventListener(this.eventName, this, this.eventOptions); 18 } 19 disconnect() { 20 this.eventTarget.removeEventListener(this.eventName, this, this.eventOptions); 21 } 22 bindingConnected(binding) { 23 this.unorderedBindings.add(binding); 24 } 25 bindingDisconnected(binding) { 26 this.unorderedBindings.delete(binding); 27 } 28 handleEvent(event) { 29 const extendedEvent = extendEvent(event); 30 for (const binding of this.bindings) { 31 if (extendedEvent.immediatePropagationStopped) { 32 break; 33 } 34 else { 35 binding.handleEvent(extendedEvent); 36 } 37 } 38 } 39 hasBindings() { 40 return this.unorderedBindings.size > 0; 41 } 42 get bindings() { 43 return Array.from(this.unorderedBindings).sort((left, right) => { 44 const leftIndex = left.index, rightIndex = right.index; 45 return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0; 46 }); 47 } 48} 49function extendEvent(event) { 50 if ("immediatePropagationStopped" in event) { 51 return event; 52 } 53 else { 54 const { stopImmediatePropagation } = event; 55 return Object.assign(event, { 56 immediatePropagationStopped: false, 57 stopImmediatePropagation() { 58 this.immediatePropagationStopped = true; 59 stopImmediatePropagation.call(this); 60 }, 61 }); 62 } 63} 64 65class Dispatcher { 66 constructor(application) { 67 this.application = application; 68 this.eventListenerMaps = new Map(); 69 this.started = false; 70 } 71 start() { 72 if (!this.started) { 73 this.started = true; 74 this.eventListeners.forEach((eventListener) => eventListener.connect()); 75 } 76 } 77 stop() { 78 if (this.started) { 79 this.started = false; 80 this.eventListeners.forEach((eventListener) => eventListener.disconnect()); 81 } 82 } 83 get eventListeners() { 84 return Array.from(this.eventListenerMaps.values()).reduce((listeners, map) => listeners.concat(Array.from(map.values())), []); 85 } 86 bindingConnected(binding) { 87 this.fetchEventListenerForBinding(binding).bindingConnected(binding); 88 } 89 bindingDisconnected(binding, clearEventListeners = false) { 90 this.fetchEventListenerForBinding(binding).bindingDisconnected(binding); 91 if (clearEventListeners) 92 this.clearEventListenersForBinding(binding); 93 } 94 handleError(error, message, detail = {}) { 95 this.application.handleError(error, `Error ${message}`, detail); 96 } 97 clearEventListenersForBinding(binding) { 98 const eventListener = this.fetchEventListenerForBinding(binding); 99 if (!eventListener.hasBindings()) { 100 eventListener.disconnect(); 101 this.removeMappedEventListenerFor(binding); 102 } 103 } 104 removeMappedEventListenerFor(binding) { 105 const { eventTarget, eventName, eventOptions } = binding; 106 const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget); 107 const cacheKey = this.cacheKey(eventName, eventOptions); 108 eventListenerMap.delete(cacheKey); 109 if (eventListenerMap.size == 0) 110 this.eventListenerMaps.delete(eventTarget); 111 } 112 fetchEventListenerForBinding(binding) { 113 const { eventTarget, eventName, eventOptions } = binding; 114 return this.fetchEventListener(eventTarget, eventName, eventOptions); 115 } 116 fetchEventListener(eventTarget, eventName, eventOptions) { 117 const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget); 118 const cacheKey = this.cacheKey(eventName, eventOptions); 119 let eventListener = eventListenerMap.get(cacheKey); 120 if (!eventListener) { 121 eventListener = this.createEventListener(eventTarget, eventName, eventOptions); 122 eventListenerMap.set(cacheKey, eventListener); 123 } 124 return eventListener; 125 } 126 createEventListener(eventTarget, eventName, eventOptions) { 127 const eventListener = new EventListener(eventTarget, eventName, eventOptions); 128 if (this.started) { 129 eventListener.connect(); 130 } 131 return eventListener; 132 } 133 fetchEventListenerMapForEventTarget(eventTarget) { 134 let eventListenerMap = this.eventListenerMaps.get(eventTarget); 135 if (!eventListenerMap) { 136 eventListenerMap = new Map(); 137 this.eventListenerMaps.set(eventTarget, eventListenerMap); 138 } 139 return eventListenerMap; 140 } 141 cacheKey(eventName, eventOptions) { 142 const parts = [eventName]; 143 Object.keys(eventOptions) 144 .sort() 145 .forEach((key) => { 146 parts.push(`${eventOptions[key] ? "" : "!"}${key}`); 147 }); 148 return parts.join(":"); 149 } 150} 151 152const defaultActionDescriptorFilters = { 153 stop({ event, value }) { 154 if (value) 155 event.stopPropagation(); 156 return true; 157 }, 158 prevent({ event, value }) { 159 if (value) 160 event.preventDefault(); 161 return true; 162 }, 163 self({ event, value, element }) { 164 if (value) { 165 return element === event.target; 166 } 167 else { 168 return true; 169 } 170 }, 171}; 172const descriptorPattern = /^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/; 173function parseActionDescriptorString(descriptorString) { 174 const source = descriptorString.trim(); 175 const matches = source.match(descriptorPattern) || []; 176 let eventName = matches[2]; 177 let keyFilter = matches[3]; 178 if (keyFilter && !["keydown", "keyup", "keypress"].includes(eventName)) { 179 eventName += `.${keyFilter}`; 180 keyFilter = ""; 181 } 182 return { 183 eventTarget: parseEventTarget(matches[4]), 184 eventName, 185 eventOptions: matches[7] ? parseEventOptions(matches[7]) : {}, 186 identifier: matches[5], 187 methodName: matches[6], 188 keyFilter: matches[1] || keyFilter, 189 }; 190} 191function parseEventTarget(eventTargetName) { 192 if (eventTargetName == "window") { 193 return window; 194 } 195 else if (eventTargetName == "document") { 196 return document; 197 } 198} 199function parseEventOptions(eventOptions) { 200 return eventOptions 201 .split(":") 202 .reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {}); 203} 204function stringifyEventTarget(eventTarget) { 205 if (eventTarget == window) { 206 return "window"; 207 } 208 else if (eventTarget == document) { 209 return "document"; 210 } 211} 212 213function camelize(value) { 214 return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase()); 215} 216function namespaceCamelize(value) { 217 return camelize(value.replace(/--/g, "-").replace(/__/g, "_")); 218} 219function capitalize(value) { 220 return value.charAt(0).toUpperCase() + value.slice(1); 221} 222function dasherize(value) { 223 return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`); 224} 225function tokenize(value) { 226 return value.match(/[^\s]+/g) || []; 227} 228 229function isSomething(object) { 230 return object !== null && object !== undefined; 231} 232function hasProperty(object, property) { 233 return Object.prototype.hasOwnProperty.call(object, property); 234} 235 236const allModifiers = ["meta", "ctrl", "alt", "shift"]; 237class Action { 238 constructor(element, index, descriptor, schema) { 239 this.element = element; 240 this.index = index; 241 this.eventTarget = descriptor.eventTarget || element; 242 this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name"); 243 this.eventOptions = descriptor.eventOptions || {}; 244 this.identifier = descriptor.identifier || error("missing identifier"); 245 this.methodName = descriptor.methodName || error("missing method name"); 246 this.keyFilter = descriptor.keyFilter || ""; 247 this.schema = schema; 248 } 249 static forToken(token, schema) { 250 return new this(token.element, token.index, parseActionDescriptorString(token.content), schema); 251 } 252 toString() { 253 const eventFilter = this.keyFilter ? `.${this.keyFilter}` : ""; 254 const eventTarget = this.eventTargetName ? `@${this.eventTargetName}` : ""; 255 return `${this.eventName}${eventFilter}${eventTarget}->${this.identifier}#${this.methodName}`; 256 } 257 shouldIgnoreKeyboardEvent(event) { 258 if (!this.keyFilter) { 259 return false; 260 } 261 const filters = this.keyFilter.split("+"); 262 if (this.keyFilterDissatisfied(event, filters)) { 263 return true; 264 } 265 const standardFilter = filters.filter((key) => !allModifiers.includes(key))[0]; 266 if (!standardFilter) { 267 return false; 268 } 269 if (!hasProperty(this.keyMappings, standardFilter)) { 270 error(`contains unknown key filter: ${this.keyFilter}`); 271 } 272 return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase(); 273 } 274 shouldIgnoreMouseEvent(event) { 275 if (!this.keyFilter) { 276 return false; 277 } 278 const filters = [this.keyFilter]; 279 if (this.keyFilterDissatisfied(event, filters)) { 280 return true; 281 } 282 return false; 283 } 284 get params() { 285 const params = {}; 286 const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i"); 287 for (const { name, value } of Array.from(this.element.attributes)) { 288 const match = name.match(pattern); 289 const key = match && match[1]; 290 if (key) { 291 params[camelize(key)] = typecast(value); 292 } 293 } 294 return params; 295 } 296 get eventTargetName() { 297 return stringifyEventTarget(this.eventTarget); 298 } 299 get keyMappings() { 300 return this.schema.keyMappings; 301 } 302 keyFilterDissatisfied(event, filters) { 303 const [meta, ctrl, alt, shift] = allModifiers.map((modifier) => filters.includes(modifier)); 304 return event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift; 305 } 306} 307const defaultEventNames = { 308 a: () => "click", 309 button: () => "click", 310 form: () => "submit", 311 details: () => "toggle", 312 input: (e) => (e.getAttribute("type") == "submit" ? "click" : "input"), 313 select: () => "change", 314 textarea: () => "input", 315}; 316function getDefaultEventNameForElement(element) { 317 const tagName = element.tagName.toLowerCase(); 318 if (tagName in defaultEventNames) { 319 return defaultEventNames[tagName](element); 320 } 321} 322function error(message) { 323 throw new Error(message); 324} 325function typecast(value) { 326 try { 327 return JSON.parse(value); 328 } 329 catch (o_O) { 330 return value; 331 } 332} 333 334class Binding { 335 constructor(context, action) { 336 this.context = context; 337 this.action = action; 338 } 339 get index() { 340 return this.action.index; 341 } 342 get eventTarget() { 343 return this.action.eventTarget; 344 } 345 get eventOptions() { 346 return this.action.eventOptions; 347 } 348 get identifier() { 349 return this.context.identifier; 350 } 351 handleEvent(event) { 352 const actionEvent = this.prepareActionEvent(event); 353 if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(actionEvent)) { 354 this.invokeWithEvent(actionEvent); 355 } 356 } 357 get eventName() { 358 return this.action.eventName; 359 } 360 get method() { 361 const method = this.controller[this.methodName]; 362 if (typeof method == "function") { 363 return method; 364 } 365 throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`); 366 } 367 applyEventModifiers(event) { 368 const { element } = this.action; 369 const { actionDescriptorFilters } = this.context.application; 370 const { controller } = this.context; 371 let passes = true; 372 for (const [name, value] of Object.entries(this.eventOptions)) { 373 if (name in actionDescriptorFilters) { 374 const filter = actionDescriptorFilters[name]; 375 passes = passes && filter({ name, value, event, element, controller }); 376 } 377 else { 378 continue; 379 } 380 } 381 return passes; 382 } 383 prepareActionEvent(event) { 384 return Object.assign(event, { params: this.action.params }); 385 } 386 invokeWithEvent(event) { 387 const { target, currentTarget } = event; 388 try { 389 this.method.call(this.controller, event); 390 this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName }); 391 } 392 catch (error) { 393 const { identifier, controller, element, index } = this; 394 const detail = { identifier, controller, element, index, event }; 395 this.context.handleError(error, `invoking action "${this.action}"`, detail); 396 } 397 } 398 willBeInvokedByEvent(event) { 399 const eventTarget = event.target; 400 if (event instanceof KeyboardEvent && this.action.shouldIgnoreKeyboardEvent(event)) { 401 return false; 402 } 403 if (event instanceof MouseEvent && this.action.shouldIgnoreMouseEvent(event)) { 404 return false; 405 } 406 if (this.element === eventTarget) { 407 return true; 408 } 409 else if (eventTarget instanceof Element && this.element.contains(eventTarget)) { 410 return this.scope.containsElement(eventTarget); 411 } 412 else { 413 return this.scope.containsElement(this.action.element); 414 } 415 } 416 get controller() { 417 return this.context.controller; 418 } 419 get methodName() { 420 return this.action.methodName; 421 } 422 get element() { 423 return this.scope.element; 424 } 425 get scope() { 426 return this.context.scope; 427 } 428} 429 430class ElementObserver { 431 constructor(element, delegate) { 432 this.mutationObserverInit = { attributes: true, childList: true, subtree: true }; 433 this.element = element; 434 this.started = false; 435 this.delegate = delegate; 436 this.elements = new Set(); 437 this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations)); 438 } 439 start() { 440 if (!this.started) { 441 this.started = true; 442 this.mutationObserver.observe(this.element, this.mutationObserverInit); 443 this.refresh(); 444 } 445 } 446 pause(callback) { 447 if (this.started) { 448 this.mutationObserver.disconnect(); 449 this.started = false; 450 } 451 callback(); 452 if (!this.started) { 453 this.mutationObserver.observe(this.element, this.mutationObserverInit); 454 this.started = true; 455 } 456 } 457 stop() { 458 if (this.started) { 459 this.mutationObserver.takeRecords(); 460 this.mutationObserver.disconnect(); 461 this.started = false; 462 } 463 } 464 refresh() { 465 if (this.started) { 466 const matches = new Set(this.matchElementsInTree()); 467 for (const element of Array.from(this.elements)) { 468 if (!matches.has(element)) { 469 this.removeElement(element); 470 } 471 } 472 for (const element of Array.from(matches)) { 473 this.addElement(element); 474 } 475 } 476 } 477 processMutations(mutations) { 478 if (this.started) { 479 for (const mutation of mutations) { 480 this.processMutation(mutation); 481 } 482 } 483 } 484 processMutation(mutation) { 485 if (mutation.type == "attributes") { 486 this.processAttributeChange(mutation.target, mutation.attributeName); 487 } 488 else if (mutation.type == "childList") { 489 this.processRemovedNodes(mutation.removedNodes); 490 this.processAddedNodes(mutation.addedNodes); 491 } 492 } 493 processAttributeChange(element, attributeName) { 494 if (this.elements.has(element)) { 495 if (this.delegate.elementAttributeChanged && this.matchElement(element)) { 496 this.delegate.elementAttributeChanged(element, attributeName); 497 } 498 else { 499 this.removeElement(element); 500 } 501 } 502 else if (this.matchElement(element)) { 503 this.addElement(element); 504 } 505 } 506 processRemovedNodes(nodes) { 507 for (const node of Array.from(nodes)) { 508 const element = this.elementFromNode(node); 509 if (element) { 510 this.processTree(element, this.removeElement); 511 } 512 } 513 } 514 processAddedNodes(nodes) { 515 for (const node of Array.from(nodes)) { 516 const element = this.elementFromNode(node); 517 if (element && this.elementIsActive(element)) { 518 this.processTree(element, this.addElement); 519 } 520 } 521 } 522 matchElement(element) { 523 return this.delegate.matchElement(element); 524 } 525 matchElementsInTree(tree = this.element) { 526 return this.delegate.matchElementsInTree(tree); 527 } 528 processTree(tree, processor) { 529 for (const element of this.matchElementsInTree(tree)) { 530 processor.call(this, element); 531 } 532 } 533 elementFromNode(node) { 534 if (node.nodeType == Node.ELEMENT_NODE) { 535 return node; 536 } 537 } 538 elementIsActive(element) { 539 if (element.isConnected != this.element.isConnected) { 540 return false; 541 } 542 else { 543 return this.element.contains(element); 544 } 545 } 546 addElement(element) { 547 if (!this.elements.has(element)) { 548 if (this.elementIsActive(element)) { 549 this.elements.add(element); 550 if (this.delegate.elementMatched) { 551 this.delegate.elementMatched(element); 552 } 553 } 554 } 555 } 556 removeElement(element) { 557 if (this.elements.has(element)) { 558 this.elements.delete(element); 559 if (this.delegate.elementUnmatched) { 560 this.delegate.elementUnmatched(element); 561 } 562 } 563 } 564} 565 566class AttributeObserver { 567 constructor(element, attributeName, delegate) { 568 this.attributeName = attributeName; 569 this.delegate = delegate; 570 this.elementObserver = new ElementObserver(element, this); 571 } 572 get element() { 573 return this.elementObserver.element; 574 } 575 get selector() { 576 return `[${this.attributeName}]`; 577 } 578 start() { 579 this.elementObserver.start(); 580 } 581 pause(callback) { 582 this.elementObserver.pause(callback); 583 } 584 stop() { 585 this.elementObserver.stop(); 586 } 587 refresh() { 588 this.elementObserver.refresh(); 589 } 590 get started() { 591 return this.elementObserver.started; 592 } 593 matchElement(element) { 594 return element.hasAttribute(this.attributeName); 595 } 596 matchElementsInTree(tree) { 597 const match = this.matchElement(tree) ? [tree] : []; 598 const matches = Array.from(tree.querySelectorAll(this.selector)); 599 return match.concat(matches); 600 } 601 elementMatched(element) { 602 if (this.delegate.elementMatchedAttribute) { 603 this.delegate.elementMatchedAttribute(element, this.attributeName); 604 } 605 } 606 elementUnmatched(element) { 607 if (this.delegate.elementUnmatchedAttribute) { 608 this.delegate.elementUnmatchedAttribute(element, this.attributeName); 609 } 610 } 611 elementAttributeChanged(element, attributeName) { 612 if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) { 613 this.delegate.elementAttributeValueChanged(element, attributeName); 614 } 615 } 616} 617 618function add(map, key, value) { 619 fetch(map, key).add(value); 620} 621function del(map, key, value) { 622 fetch(map, key).delete(value); 623 prune(map, key); 624} 625function fetch(map, key) { 626 let values = map.get(key); 627 if (!values) { 628 values = new Set(); 629 map.set(key, values); 630 } 631 return values; 632} 633function prune(map, key) { 634 const values = map.get(key); 635 if (values != null && values.size == 0) { 636 map.delete(key); 637 } 638} 639 640class Multimap { 641 constructor() { 642 this.valuesByKey = new Map(); 643 } 644 get keys() { 645 return Array.from(this.valuesByKey.keys()); 646 } 647 get values() { 648 const sets = Array.from(this.valuesByKey.values()); 649 return sets.reduce((values, set) => values.concat(Array.from(set)), []); 650 } 651 get size() { 652 const sets = Array.from(this.valuesByKey.values()); 653 return sets.reduce((size, set) => size + set.size, 0); 654 } 655 add(key, value) { 656 add(this.valuesByKey, key, value); 657 } 658 delete(key, value) { 659 del(this.valuesByKey, key, value); 660 } 661 has(key, value) { 662 const values = this.valuesByKey.get(key); 663 return values != null && values.has(value); 664 } 665 hasKey(key) { 666 return this.valuesByKey.has(key); 667 } 668 hasValue(value) { 669 const sets = Array.from(this.valuesByKey.values()); 670 return sets.some((set) => set.has(value)); 671 } 672 getValuesForKey(key) { 673 const values = this.valuesByKey.get(key); 674 return values ? Array.from(values) : []; 675 } 676 getKeysForValue(value) { 677 return Array.from(this.valuesByKey) 678 .filter(([_key, values]) => values.has(value)) 679 .map(([key, _values]) => key); 680 } 681} 682 683class IndexedMultimap extends Multimap { 684 constructor() { 685 super(); 686 this.keysByValue = new Map(); 687 } 688 get values() { 689 return Array.from(this.keysByValue.keys()); 690 } 691 add(key, value) { 692 super.add(key, value); 693 add(this.keysByValue, value, key); 694 } 695 delete(key, value) { 696 super.delete(key, value); 697 del(this.keysByValue, value, key); 698 } 699 hasValue(value) { 700 return this.keysByValue.has(value); 701 } 702 getKeysForValue(value) { 703 const set = this.keysByValue.get(value); 704 return set ? Array.from(set) : []; 705 } 706} 707 708class SelectorObserver { 709 constructor(element, selector, delegate, details) { 710 this._selector = selector; 711 this.details = details; 712 this.elementObserver = new ElementObserver(element, this); 713 this.delegate = delegate; 714 this.matchesByElement = new Multimap(); 715 } 716 get started() { 717 return this.elementObserver.started; 718 } 719 get selector() { 720 return this._selector; 721 } 722 set selector(selector) { 723 this._selector = selector; 724 this.refresh(); 725 } 726 start() { 727 this.elementObserver.start(); 728 } 729 pause(callback) { 730 this.elementObserver.pause(callback); 731 } 732 stop() { 733 this.elementObserver.stop(); 734 } 735 refresh() { 736 this.elementObserver.refresh(); 737 } 738 get element() { 739 return this.elementObserver.element; 740 } 741 matchElement(element) { 742 const { selector } = this; 743 if (selector) { 744 const matches = element.matches(selector); 745 if (this.delegate.selectorMatchElement) { 746 return matches && this.delegate.selectorMatchElement(element, this.details); 747 } 748 return matches; 749 } 750 else { 751 return false; 752 } 753 } 754 matchElementsInTree(tree) { 755 const { selector } = this; 756 if (selector) { 757 const match = this.matchElement(tree) ? [tree] : []; 758 const matches = Array.from(tree.querySelectorAll(selector)).filter((match) => this.matchElement(match)); 759 return match.concat(matches); 760 } 761 else { 762 return []; 763 } 764 } 765 elementMatched(element) { 766 const { selector } = this; 767 if (selector) { 768 this.selectorMatched(element, selector); 769 } 770 } 771 elementUnmatched(element) { 772 const selectors = this.matchesByElement.getKeysForValue(element); 773 for (const selector of selectors) { 774 this.selectorUnmatched(element, selector); 775 } 776 } 777 elementAttributeChanged(element, _attributeName) { 778 const { selector } = this; 779 if (selector) { 780 const matches = this.matchElement(element); 781 const matchedBefore = this.matchesByElement.has(selector, element); 782 if (matches && !matchedBefore) { 783 this.selectorMatched(element, selector); 784 } 785 else if (!matches && matchedBefore) { 786 this.selectorUnmatched(element, selector); 787 } 788 } 789 } 790 selectorMatched(element, selector) { 791 this.delegate.selectorMatched(element, selector, this.details); 792 this.matchesByElement.add(selector, element); 793 } 794 selectorUnmatched(element, selector) { 795 this.delegate.selectorUnmatched(element, selector, this.details); 796 this.matchesByElement.delete(selector, element); 797 } 798} 799 800class StringMapObserver { 801 constructor(element, delegate) { 802 this.element = element; 803 this.delegate = delegate; 804 this.started = false; 805 this.stringMap = new Map(); 806 this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations)); 807 } 808 start() { 809 if (!this.started) { 810 this.started = true; 811 this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true }); 812 this.refresh(); 813 } 814 } 815 stop() { 816 if (this.started) { 817 this.mutationObserver.takeRecords(); 818 this.mutationObserver.disconnect(); 819 this.started = false; 820 } 821 } 822 refresh() { 823 if (this.started) { 824 for (const attributeName of this.knownAttributeNames) { 825 this.refreshAttribute(attributeName, null); 826 } 827 } 828 } 829 processMutations(mutations) { 830 if (this.started) { 831 for (const mutation of mutations) { 832 this.processMutation(mutation); 833 } 834 } 835 } 836 processMutation(mutation) { 837 const attributeName = mutation.attributeName; 838 if (attributeName) { 839 this.refreshAttribute(attributeName, mutation.oldValue); 840 } 841 } 842 refreshAttribute(attributeName, oldValue) { 843 const key = this.delegate.getStringMapKeyForAttribute(attributeName); 844 if (key != null) { 845 if (!this.stringMap.has(attributeName)) { 846 this.stringMapKeyAdded(key, attributeName); 847 } 848 const value = this.element.getAttribute(attributeName); 849 if (this.stringMap.get(attributeName) != value) { 850 this.stringMapValueChanged(value, key, oldValue); 851 } 852 if (value == null) { 853 const oldValue = this.stringMap.get(attributeName); 854 this.stringMap.delete(attributeName); 855 if (oldValue) 856 this.stringMapKeyRemoved(key, attributeName, oldValue); 857 } 858 else { 859 this.stringMap.set(attributeName, value); 860 } 861 } 862 } 863 stringMapKeyAdded(key, attributeName) { 864 if (this.delegate.stringMapKeyAdded) { 865 this.delegate.stringMapKeyAdded(key, attributeName); 866 } 867 } 868 stringMapValueChanged(value, key, oldValue) { 869 if (this.delegate.stringMapValueChanged) { 870 this.delegate.stringMapValueChanged(value, key, oldValue); 871 } 872 } 873 stringMapKeyRemoved(key, attributeName, oldValue) { 874 if (this.delegate.stringMapKeyRemoved) { 875 this.delegate.stringMapKeyRemoved(key, attributeName, oldValue); 876 } 877 } 878 get knownAttributeNames() { 879 return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames))); 880 } 881 get currentAttributeNames() { 882 return Array.from(this.element.attributes).map((attribute) => attribute.name); 883 } 884 get recordedAttributeNames() { 885 return Array.from(this.stringMap.keys()); 886 } 887} 888 889class TokenListObserver { 890 constructor(element, attributeName, delegate) { 891 this.attributeObserver = new AttributeObserver(element, attributeName, this); 892 this.delegate = delegate; 893 this.tokensByElement = new Multimap(); 894 } 895 get started() { 896 return this.attributeObserver.started; 897 } 898 start() { 899 this.attributeObserver.start(); 900 } 901 pause(callback) { 902 this.attributeObserver.pause(callback); 903 } 904 stop() { 905 this.attributeObserver.stop(); 906 } 907 refresh() { 908 this.attributeObserver.refresh(); 909 } 910 get element() { 911 return this.attributeObserver.element; 912 } 913 get attributeName() { 914 return this.attributeObserver.attributeName; 915 } 916 elementMatchedAttribute(element) { 917 this.tokensMatched(this.readTokensForElement(element)); 918 } 919 elementAttributeValueChanged(element) { 920 const [unmatchedTokens, matchedTokens] = this.refreshTokensForElement(element); 921 this.tokensUnmatched(unmatchedTokens); 922 this.tokensMatched(matchedTokens); 923 } 924 elementUnmatchedAttribute(element) { 925 this.tokensUnmatched(this.tokensByElement.getValuesForKey(element)); 926 } 927 tokensMatched(tokens) { 928 tokens.forEach((token) => this.tokenMatched(token)); 929 } 930 tokensUnmatched(tokens) { 931 tokens.forEach((token) => this.tokenUnmatched(token)); 932 } 933 tokenMatched(token) { 934 this.delegate.tokenMatched(token); 935 this.tokensByElement.add(token.element, token); 936 } 937 tokenUnmatched(token) { 938 this.delegate.tokenUnmatched(token); 939 this.tokensByElement.delete(token.element, token); 940 } 941 refreshTokensForElement(element) { 942 const previousTokens = this.tokensByElement.getValuesForKey(element); 943 const currentTokens = this.readTokensForElement(element); 944 const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken)); 945 if (firstDifferingIndex == -1) { 946 return [[], []]; 947 } 948 else { 949 return [previousTokens.slice(firstDifferingIndex), currentTokens.slice(firstDifferingIndex)]; 950 } 951 } 952 readTokensForElement(element) { 953 const attributeName = this.attributeName; 954 const tokenString = element.getAttribute(attributeName) || ""; 955 return parseTokenString(tokenString, element, attributeName); 956 } 957} 958function parseTokenString(tokenString, element, attributeName) { 959 return tokenString 960 .trim() 961 .split(/\s+/) 962 .filter((content) => content.length) 963 .map((content, index) => ({ element, attributeName, content, index })); 964} 965function zip(left, right) { 966 const length = Math.max(left.length, right.length); 967 return Array.from({ length }, (_, index) => [left[index], right[index]]); 968} 969function tokensAreEqual(left, right) { 970 return left && right && left.index == right.index && left.content == right.content; 971} 972 973class ValueListObserver { 974 constructor(element, attributeName, delegate) { 975 this.tokenListObserver = new TokenListObserver(element, attributeName, this); 976 this.delegate = delegate; 977 this.parseResultsByToken = new WeakMap(); 978 this.valuesByTokenByElement = new WeakMap(); 979 } 980 get started() { 981 return this.tokenListObserver.started; 982 } 983 start() { 984 this.tokenListObserver.start(); 985 } 986 stop() { 987 this.tokenListObserver.stop(); 988 } 989 refresh() { 990 this.tokenListObserver.refresh(); 991 } 992 get element() { 993 return this.tokenListObserver.element; 994 } 995 get attributeName() { 996 return this.tokenListObserver.attributeName; 997 } 998 tokenMatched(token) { 999 const { element } = token; 1000 const { value } = this.fetchParseResultForToken(token); 1001 if (value) { 1002 this.fetchValuesByTokenForElement(element).set(token, value); 1003 this.delegate.elementMatchedValue(element, value); 1004 } 1005 } 1006 tokenUnmatched(token) { 1007 const { element } = token; 1008 const { value } = this.fetchParseResultForToken(token); 1009 if (value) { 1010 this.fetchValuesByTokenForElement(element).delete(token); 1011 this.delegate.elementUnmatchedValue(element, value); 1012 } 1013 } 1014 fetchParseResultForToken(token) { 1015 let parseResult = this.parseResultsByToken.get(token); 1016 if (!parseResult) { 1017 parseResult = this.parseToken(token); 1018 this.parseResultsByToken.set(token, parseResult); 1019 } 1020 return parseResult; 1021 } 1022 fetchValuesByTokenForElement(element) { 1023 let valuesByToken = this.valuesByTokenByElement.get(element); 1024 if (!valuesByToken) { 1025 valuesByToken = new Map(); 1026 this.valuesByTokenByElement.set(element, valuesByToken); 1027 } 1028 return valuesByToken; 1029 } 1030 parseToken(token) { 1031 try { 1032 const value = this.delegate.parseValueForToken(token); 1033 return { value }; 1034 } 1035 catch (error) { 1036 return { error }; 1037 } 1038 } 1039} 1040 1041class BindingObserver { 1042 constructor(context, delegate) { 1043 this.context = context; 1044 this.delegate = delegate; 1045 this.bindingsByAction = new Map(); 1046 } 1047 start() { 1048 if (!this.valueListObserver) { 1049 this.valueListObserver = new ValueListObserver(this.element, this.actionAttribute, this); 1050 this.valueListObserver.start(); 1051 } 1052 } 1053 stop() { 1054 if (this.valueListObserver) { 1055 this.valueListObserver.stop(); 1056 delete this.valueListObserver; 1057 this.disconnectAllActions(); 1058 } 1059 } 1060 get element() { 1061 return this.context.element; 1062 } 1063 get identifier() { 1064 return this.context.identifier; 1065 } 1066 get actionAttribute() { 1067 return this.schema.actionAttribute; 1068 } 1069 get schema() { 1070 return this.context.schema; 1071 } 1072 get bindings() { 1073 return Array.from(this.bindingsByAction.values()); 1074 } 1075 connectAction(action) { 1076 const binding = new Binding(this.context, action); 1077 this.bindingsByAction.set(action, binding); 1078 this.delegate.bindingConnected(binding); 1079 } 1080 disconnectAction(action) { 1081 const binding = this.bindingsByAction.get(action); 1082 if (binding) { 1083 this.bindingsByAction.delete(action); 1084 this.delegate.bindingDisconnected(binding); 1085 } 1086 } 1087 disconnectAllActions() { 1088 this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true)); 1089 this.bindingsByAction.clear(); 1090 } 1091 parseValueForToken(token) { 1092 const action = Action.forToken(token, this.schema); 1093 if (action.identifier == this.identifier) { 1094 return action; 1095 } 1096 } 1097 elementMatchedValue(element, action) { 1098 this.connectAction(action); 1099 } 1100 elementUnmatchedValue(element, action) { 1101 this.disconnectAction(action); 1102 } 1103} 1104 1105class ValueObserver { 1106 constructor(context, receiver) { 1107 this.context = context; 1108 this.receiver = receiver; 1109 this.stringMapObserver = new StringMapObserver(this.element, this); 1110 this.valueDescriptorMap = this.controller.valueDescriptorMap; 1111 } 1112 start() { 1113 this.stringMapObserver.start(); 1114 this.invokeChangedCallbacksForDefaultValues(); 1115 } 1116 stop() { 1117 this.stringMapObserver.stop(); 1118 } 1119 get element() { 1120 return this.context.element; 1121 } 1122 get controller() { 1123 return this.context.controller; 1124 } 1125 getStringMapKeyForAttribute(attributeName) { 1126 if (attributeName in this.valueDescriptorMap) { 1127 return this.valueDescriptorMap[attributeName].name; 1128 } 1129 } 1130 stringMapKeyAdded(key, attributeName) { 1131 const descriptor = this.valueDescriptorMap[attributeName]; 1132 if (!this.hasValue(key)) { 1133 this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), descriptor.writer(descriptor.defaultValue)); 1134 } 1135 } 1136 stringMapValueChanged(value, name, oldValue) { 1137 const descriptor = this.valueDescriptorNameMap[name]; 1138 if (value === null) 1139 return; 1140 if (oldValue === null) { 1141 oldValue = descriptor.writer(descriptor.defaultValue); 1142 } 1143 this.invokeChangedCallback(name, value, oldValue); 1144 } 1145 stringMapKeyRemoved(key, attributeName, oldValue) { 1146 const descriptor = this.valueDescriptorNameMap[key]; 1147 if (this.hasValue(key)) { 1148 this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), oldValue); 1149 } 1150 else { 1151 this.invokeChangedCallback(key, descriptor.writer(descriptor.defaultValue), oldValue); 1152 } 1153 } 1154 invokeChangedCallbacksForDefaultValues() { 1155 for (const { key, name, defaultValue, writer } of this.valueDescriptors) { 1156 if (defaultValue != undefined && !this.controller.data.has(key)) { 1157 this.invokeChangedCallback(name, writer(defaultValue), undefined); 1158 } 1159 } 1160 } 1161 invokeChangedCallback(name, rawValue, rawOldValue) { 1162 const changedMethodName = `${name}Changed`; 1163 const changedMethod = this.receiver[changedMethodName]; 1164 if (typeof changedMethod == "function") { 1165 const descriptor = this.valueDescriptorNameMap[name]; 1166 try { 1167 const value = descriptor.reader(rawValue); 1168 let oldValue = rawOldValue; 1169 if (rawOldValue) { 1170 oldValue = descriptor.reader(rawOldValue); 1171 } 1172 changedMethod.call(this.receiver, value, oldValue); 1173 } 1174 catch (error) { 1175 if (error instanceof TypeError) { 1176 error.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`; 1177 } 1178 throw error; 1179 } 1180 } 1181 } 1182 get valueDescriptors() { 1183 const { valueDescriptorMap } = this; 1184 return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]); 1185 } 1186 get valueDescriptorNameMap() { 1187 const descriptors = {}; 1188 Object.keys(this.valueDescriptorMap).forEach((key) => { 1189 const descriptor = this.valueDescriptorMap[key]; 1190 descriptors[descriptor.name] = descriptor; 1191 }); 1192 return descriptors; 1193 } 1194 hasValue(attributeName) { 1195 const descriptor = this.valueDescriptorNameMap[attributeName]; 1196 const hasMethodName = `has${capitalize(descriptor.name)}`; 1197 return this.receiver[hasMethodName]; 1198 } 1199} 1200 1201class TargetObserver { 1202 constructor(context, delegate) { 1203 this.context = context; 1204 this.delegate = delegate; 1205 this.targetsByName = new Multimap(); 1206 } 1207 start() { 1208 if (!this.tokenListObserver) { 1209 this.tokenListObserver = new TokenListObserver(this.element, this.attributeName, this); 1210 this.tokenListObserver.start(); 1211 } 1212 } 1213 stop() { 1214 if (this.tokenListObserver) { 1215 this.disconnectAllTargets(); 1216 this.tokenListObserver.stop(); 1217 delete this.tokenListObserver; 1218 } 1219 } 1220 tokenMatched({ element, content: name }) { 1221 if (this.scope.containsElement(element)) { 1222 this.connectTarget(element, name); 1223 } 1224 } 1225 tokenUnmatched({ element, content: name }) { 1226 this.disconnectTarget(element, name); 1227 } 1228 connectTarget(element, name) { 1229 var _a; 1230 if (!this.targetsByName.has(name, element)) { 1231 this.targetsByName.add(name, element); 1232 (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetConnected(element, name)); 1233 } 1234 } 1235 disconnectTarget(element, name) { 1236 var _a; 1237 if (this.targetsByName.has(name, element)) { 1238 this.targetsByName.delete(name, element); 1239 (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetDisconnected(element, name)); 1240 } 1241 } 1242 disconnectAllTargets() { 1243 for (const name of this.targetsByName.keys) { 1244 for (const element of this.targetsByName.getValuesForKey(name)) { 1245 this.disconnectTarget(element, name); 1246 } 1247 } 1248 } 1249 get attributeName() { 1250 return `data-${this.context.identifier}-target`; 1251 } 1252 get element() { 1253 return this.context.element; 1254 } 1255 get scope() { 1256 return this.context.scope; 1257 } 1258} 1259 1260function readInheritableStaticArrayValues(constructor, propertyName) { 1261 const ancestors = getAncestorsForConstructor(constructor); 1262 return Array.from(ancestors.reduce((values, constructor) => { 1263 getOwnStaticArrayValues(constructor, propertyName).forEach((name) => values.add(name)); 1264 return values; 1265 }, new Set())); 1266} 1267function readInheritableStaticObjectPairs(constructor, propertyName) { 1268 const ancestors = getAncestorsForConstructor(constructor); 1269 return ancestors.reduce((pairs, constructor) => { 1270 pairs.push(...getOwnStaticObjectPairs(constructor, propertyName)); 1271 return pairs; 1272 }, []); 1273} 1274function getAncestorsForConstructor(constructor) { 1275 const ancestors = []; 1276 while (constructor) { 1277 ancestors.push(constructor); 1278 constructor = Object.getPrototypeOf(constructor); 1279 } 1280 return ancestors.reverse(); 1281} 1282function getOwnStaticArrayValues(constructor, propertyName) { 1283 const definition = constructor[propertyName]; 1284 return Array.isArray(definition) ? definition : []; 1285} 1286function getOwnStaticObjectPairs(constructor, propertyName) { 1287 const definition = constructor[propertyName]; 1288 return definition ? Object.keys(definition).map((key) => [key, definition[key]]) : []; 1289} 1290 1291class OutletObserver { 1292 constructor(context, delegate) { 1293 this.started = false; 1294 this.context = context; 1295 this.delegate = delegate; 1296 this.outletsByName = new Multimap(); 1297 this.outletElementsByName = new Multimap(); 1298 this.selectorObserverMap = new Map(); 1299 this.attributeObserverMap = new Map(); 1300 } 1301 start() { 1302 if (!this.started) { 1303 this.outletDefinitions.forEach((outletName) => { 1304 this.setupSelectorObserverForOutlet(outletName); 1305 this.setupAttributeObserverForOutlet(outletName); 1306 }); 1307 this.started = true; 1308 this.dependentContexts.forEach((context) => context.refresh()); 1309 } 1310 } 1311 refresh() { 1312 this.selectorObserverMap.forEach((observer) => observer.refresh()); 1313 this.attributeObserverMap.forEach((observer) => observer.refresh()); 1314 } 1315 stop() { 1316 if (this.started) { 1317 this.started = false; 1318 this.disconnectAllOutlets(); 1319 this.stopSelectorObservers(); 1320 this.stopAttributeObservers(); 1321 } 1322 } 1323 stopSelectorObservers() { 1324 if (this.selectorObserverMap.size > 0) { 1325 this.selectorObserverMap.forEach((observer) => observer.stop()); 1326 this.selectorObserverMap.clear(); 1327 } 1328 } 1329 stopAttributeObservers() { 1330 if (this.attributeObserverMap.size > 0) { 1331 this.attributeObserverMap.forEach((observer) => observer.stop()); 1332 this.attributeObserverMap.clear(); 1333 } 1334 } 1335 selectorMatched(element, _selector, { outletName }) { 1336 const outlet = this.getOutlet(element, outletName); 1337 if (outlet) { 1338 this.connectOutlet(outlet, element, outletName); 1339 } 1340 } 1341 selectorUnmatched(element, _selector, { outletName }) { 1342 const outlet = this.getOutletFromMap(element, outletName); 1343 if (outlet) { 1344 this.disconnectOutlet(outlet, element, outletName); 1345 } 1346 } 1347 selectorMatchElement(element, { outletName }) { 1348 const selector = this.selector(outletName); 1349 const hasOutlet = this.hasOutlet(element, outletName); 1350 const hasOutletController = element.matches(`[${this.schema.controllerAttribute}~=${outletName}]`); 1351 if (selector) { 1352 return hasOutlet && hasOutletController && element.matches(selector); 1353 } 1354 else { 1355 return false; 1356 } 1357 } 1358 elementMatchedAttribute(_element, attributeName) { 1359 const outletName = this.getOutletNameFromOutletAttributeName(attributeName); 1360 if (outletName) { 1361 this.updateSelectorObserverForOutlet(outletName); 1362 } 1363 } 1364 elementAttributeValueChanged(_element, attributeName) { 1365 const outletName = this.getOutletNameFromOutletAttributeName(attributeName); 1366 if (outletName) { 1367 this.updateSelectorObserverForOutlet(outletName); 1368 } 1369 } 1370 elementUnmatchedAttribute(_element, attributeName) { 1371 const outletName = this.getOutletNameFromOutletAttributeName(attributeName); 1372 if (outletName) { 1373 this.updateSelectorObserverForOutlet(outletName); 1374 } 1375 } 1376 connectOutlet(outlet, element, outletName) { 1377 var _a; 1378 if (!this.outletElementsByName.has(outletName, element)) { 1379 this.outletsByName.add(outletName, outlet); 1380 this.outletElementsByName.add(outletName, element); 1381 (_a = this.selectorObserverMap.get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletConnected(outlet, element, outletName)); 1382 } 1383 } 1384 disconnectOutlet(outlet, element, outletName) { 1385 var _a; 1386 if (this.outletElementsByName.has(outletName, element)) { 1387 this.outletsByName.delete(outletName, outlet); 1388 this.outletElementsByName.delete(outletName, element); 1389 (_a = this.selectorObserverMap 1390 .get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletDisconnected(outlet, element, outletName)); 1391 } 1392 } 1393 disconnectAllOutlets() { 1394 for (const outletName of this.outletElementsByName.keys) { 1395 for (const element of this.outletElementsByName.getValuesForKey(outletName)) { 1396 for (const outlet of this.outletsByName.getValuesForKey(outletName)) { 1397 this.disconnectOutlet(outlet, element, outletName); 1398 } 1399 } 1400 } 1401 } 1402 updateSelectorObserverForOutlet(outletName) { 1403 const observer = this.selectorObserverMap.get(outletName); 1404 if (observer) { 1405 observer.selector = this.selector(outletName); 1406 } 1407 } 1408 setupSelectorObserverForOutlet(outletName) { 1409 const selector = this.selector(outletName); 1410 const selectorObserver = new SelectorObserver(document.body, selector, this, { outletName }); 1411 this.selectorObserverMap.set(outletName, selectorObserver); 1412 selectorObserver.start(); 1413 } 1414 setupAttributeObserverForOutlet(outletName) { 1415 const attributeName = this.attributeNameForOutletName(outletName); 1416 const attributeObserver = new AttributeObserver(this.scope.element, attributeName, this); 1417 this.attributeObserverMap.set(outletName, attributeObserver); 1418 attributeObserver.start(); 1419 } 1420 selector(outletName) { 1421 return this.scope.outlets.getSelectorForOutletName(outletName); 1422 } 1423 attributeNameForOutletName(outletName) { 1424 return this.scope.schema.outletAttributeForScope(this.identifier, outletName); 1425 } 1426 getOutletNameFromOutletAttributeName(attributeName) { 1427 return this.outletDefinitions.find((outletName) => this.attributeNameForOutletName(outletName) === attributeName); 1428 } 1429 get outletDependencies() { 1430 const dependencies = new Multimap(); 1431 this.router.modules.forEach((module) => { 1432 const constructor = module.definition.controllerConstructor; 1433 const outlets = readInheritableStaticArrayValues(constructor, "outlets"); 1434 outlets.forEach((outlet) => dependencies.add(outlet, module.identifier)); 1435 }); 1436 return dependencies; 1437 } 1438 get outletDefinitions() { 1439 return this.outletDependencies.getKeysForValue(this.identifier); 1440 } 1441 get dependentControllerIdentifiers() { 1442 return this.outletDependencies.getValuesForKey(this.identifier); 1443 } 1444 get dependentContexts() { 1445 const identifiers = this.dependentControllerIdentifiers; 1446 return this.router.contexts.filter((context) => identifiers.includes(context.identifier)); 1447 } 1448 hasOutlet(element, outletName) { 1449 return !!this.getOutlet(element, outletName) || !!this.getOutletFromMap(element, outletName); 1450 } 1451 getOutlet(element, outletName) { 1452 return this.application.getControllerForElementAndIdentifier(element, outletName); 1453 } 1454 getOutletFromMap(element, outletName) { 1455 return this.outletsByName.getValuesForKey(outletName).find((outlet) => outlet.element === element); 1456 } 1457 get scope() { 1458 return this.context.scope; 1459 } 1460 get schema() { 1461 return this.context.schema; 1462 } 1463 get identifier() { 1464 return this.context.identifier; 1465 } 1466 get application() { 1467 return this.context.application; 1468 } 1469 get router() { 1470 return this.application.router; 1471 } 1472} 1473 1474class Context { 1475 constructor(module, scope) { 1476 this.logDebugActivity = (functionName, detail = {}) => { 1477 const { identifier, controller, element } = this; 1478 detail = Object.assign({ identifier, controller, element }, detail); 1479 this.application.logDebugActivity(this.identifier, functionName, detail); 1480 }; 1481 this.module = module; 1482 this.scope = scope; 1483 this.controller = new module.controllerConstructor(this); 1484 this.bindingObserver = new BindingObserver(this, this.dispatcher); 1485 this.valueObserver = new ValueObserver(this, this.controller); 1486 this.targetObserver = new TargetObserver(this, this); 1487 this.outletObserver = new OutletObserver(this, this); 1488 try { 1489 this.controller.initialize(); 1490 this.logDebugActivity("initialize"); 1491 } 1492 catch (error) { 1493 this.handleError(error, "initializing controller"); 1494 } 1495 } 1496 connect() { 1497 this.bindingObserver.start(); 1498 this.valueObserver.start(); 1499 this.targetObserver.start(); 1500 this.outletObserver.start(); 1501 try { 1502 this.controller.connect(); 1503 this.logDebugActivity("connect"); 1504 } 1505 catch (error) { 1506 this.handleError(error, "connecting controller"); 1507 } 1508 } 1509 refresh() { 1510 this.outletObserver.refresh(); 1511 } 1512 disconnect() { 1513 try { 1514 this.controller.disconnect(); 1515 this.logDebugActivity("disconnect"); 1516 } 1517 catch (error) { 1518 this.handleError(error, "disconnecting controller"); 1519 } 1520 this.outletObserver.stop(); 1521 this.targetObserver.stop(); 1522 this.valueObserver.stop(); 1523 this.bindingObserver.stop(); 1524 } 1525 get application() { 1526 return this.module.application; 1527 } 1528 get identifier() { 1529 return this.module.identifier; 1530 } 1531 get schema() { 1532 return this.application.schema; 1533 } 1534 get dispatcher() { 1535 return this.application.dispatcher; 1536 } 1537 get element() { 1538 return this.scope.element; 1539 } 1540 get parentElement() { 1541 return this.element.parentElement; 1542 } 1543 handleError(error, message, detail = {}) { 1544 const { identifier, controller, element } = this; 1545 detail = Object.assign({ identifier, controller, element }, detail); 1546 this.application.handleError(error, `Error ${message}`, detail); 1547 } 1548 targetConnected(element, name) { 1549 this.invokeControllerMethod(`${name}TargetConnected`, element); 1550 } 1551 targetDisconnected(element, name) { 1552 this.invokeControllerMethod(`${name}TargetDisconnected`, element); 1553 } 1554 outletConnected(outlet, element, name) { 1555 this.invokeControllerMethod(`${namespaceCamelize(name)}OutletConnected`, outlet, element); 1556 } 1557 outletDisconnected(outlet, element, name) { 1558 this.invokeControllerMethod(`${namespaceCamelize(name)}OutletDisconnected`, outlet, element); 1559 } 1560 invokeControllerMethod(methodName, ...args) { 1561 const controller = this.controller; 1562 if (typeof controller[methodName] == "function") { 1563 controller[methodName](...args); 1564 } 1565 } 1566} 1567 1568function bless(constructor) { 1569 return shadow(constructor, getBlessedProperties(constructor)); 1570} 1571function shadow(constructor, properties) { 1572 const shadowConstructor = extend(constructor); 1573 const shadowProperties = getShadowProperties(constructor.prototype, properties); 1574 Object.defineProperties(shadowConstructor.prototype, shadowProperties); 1575 return shadowConstructor; 1576} 1577function getBlessedProperties(constructor) { 1578 const blessings = readInheritableStaticArrayValues(constructor, "blessings"); 1579 return blessings.reduce((blessedProperties, blessing) => { 1580 const properties = blessing(constructor); 1581 for (const key in properties) { 1582 const descriptor = blessedProperties[key] || {}; 1583 blessedProperties[key] = Object.assign(descriptor, properties[key]); 1584 } 1585 return blessedProperties; 1586 }, {}); 1587} 1588function getShadowProperties(prototype, properties) { 1589 return getOwnKeys(properties).reduce((shadowProperties, key) => { 1590 const descriptor = getShadowedDescriptor(prototype, properties, key); 1591 if (descriptor) { 1592 Object.assign(shadowProperties, { [key]: descriptor }); 1593 } 1594 return shadowProperties; 1595 }, {}); 1596} 1597function getShadowedDescriptor(prototype, properties, key) { 1598 const shadowingDescriptor = Object.getOwnPropertyDescriptor(prototype, key); 1599 const shadowedByValue = shadowingDescriptor && "value" in shadowingDescriptor; 1600 if (!shadowedByValue) { 1601 const descriptor = Object.getOwnPropertyDescriptor(properties, key).value; 1602 if (shadowingDescriptor) { 1603 descriptor.get = shadowingDescriptor.get || descriptor.get; 1604 descriptor.set = shadowingDescriptor.set || descriptor.set; 1605 } 1606 return descriptor; 1607 } 1608} 1609const getOwnKeys = (() => { 1610 if (typeof Object.getOwnPropertySymbols == "function") { 1611 return (object) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)]; 1612 } 1613 else { 1614 return Object.getOwnPropertyNames; 1615 } 1616})(); 1617const extend = (() => { 1618 function extendWithReflect(constructor) { 1619 function extended() { 1620 return Reflect.construct(constructor, arguments, new.target); 1621 } 1622 extended.prototype = Object.create(constructor.prototype, { 1623 constructor: { value: extended }, 1624 }); 1625 Reflect.setPrototypeOf(extended, constructor); 1626 return extended; 1627 } 1628 function testReflectExtension() { 1629 const a = function () { 1630 this.a.call(this); 1631 }; 1632 const b = extendWithReflect(a); 1633 b.prototype.a = function () { }; 1634 return new b(); 1635 } 1636 try { 1637 testReflectExtension(); 1638 return extendWithReflect; 1639 } 1640 catch (error) { 1641 return (constructor) => class extended extends constructor { 1642 }; 1643 } 1644})(); 1645 1646function blessDefinition(definition) { 1647 return { 1648 identifier: definition.identifier, 1649 controllerConstructor: bless(definition.controllerConstructor), 1650 }; 1651} 1652 1653class Module { 1654 constructor(application, definition) { 1655 this.application = application; 1656 this.definition = blessDefinition(definition); 1657 this.contextsByScope = new WeakMap(); 1658 this.connectedContexts = new Set(); 1659 } 1660 get identifier() { 1661 return this.definition.identifier; 1662 } 1663 get controllerConstructor() { 1664 return this.definition.controllerConstructor; 1665 } 1666 get contexts() { 1667 return Array.from(this.connectedContexts); 1668 } 1669 connectContextForScope(scope) { 1670 const context = this.fetchContextForScope(scope); 1671 this.connectedContexts.add(context); 1672 context.connect(); 1673 } 1674 disconnectContextForScope(scope) { 1675 const context = this.contextsByScope.get(scope); 1676 if (context) { 1677 this.connectedContexts.delete(context); 1678 context.disconnect(); 1679 } 1680 } 1681 fetchContextForScope(scope) { 1682 let context = this.contextsByScope.get(scope); 1683 if (!context) { 1684 context = new Context(this, scope); 1685 this.contextsByScope.set(scope, context); 1686 } 1687 return context; 1688 } 1689} 1690 1691class ClassMap { 1692 constructor(scope) { 1693 this.scope = scope; 1694 } 1695 has(name) { 1696 return this.data.has(this.getDataKey(name)); 1697 } 1698 get(name) { 1699 return this.getAll(name)[0]; 1700 } 1701 getAll(name) { 1702 const tokenString = this.data.get(this.getDataKey(name)) || ""; 1703 return tokenize(tokenString); 1704 } 1705 getAttributeName(name) { 1706 return this.data.getAttributeNameForKey(this.getDataKey(name)); 1707 } 1708 getDataKey(name) { 1709 return `${name}-class`; 1710 } 1711 get data() { 1712 return this.scope.data; 1713 } 1714} 1715 1716class DataMap { 1717 constructor(scope) { 1718 this.scope = scope; 1719 } 1720 get element() { 1721 return this.scope.element; 1722 } 1723 get identifier() { 1724 return this.scope.identifier; 1725 } 1726 get(key) { 1727 const name = this.getAttributeNameForKey(key); 1728 return this.element.getAttribute(name); 1729 } 1730 set(key, value) { 1731 const name = this.getAttributeNameForKey(key); 1732 this.element.setAttribute(name, value); 1733 return this.get(key); 1734 } 1735 has(key) { 1736 const name = this.getAttributeNameForKey(key); 1737 return this.element.hasAttribute(name); 1738 } 1739 delete(key) { 1740 if (this.has(key)) { 1741 const name = this.getAttributeNameForKey(key); 1742 this.element.removeAttribute(name); 1743 return true; 1744 } 1745 else { 1746 return false; 1747 } 1748 } 1749 getAttributeNameForKey(key) { 1750 return `data-${this.identifier}-${dasherize(key)}`; 1751 } 1752} 1753 1754class Guide { 1755 constructor(logger) { 1756 this.warnedKeysByObject = new WeakMap(); 1757 this.logger = logger; 1758 } 1759 warn(object, key, message) { 1760 let warnedKeys = this.warnedKeysByObject.get(object); 1761 if (!warnedKeys) { 1762 warnedKeys = new Set(); 1763 this.warnedKeysByObject.set(object, warnedKeys); 1764 } 1765 if (!warnedKeys.has(key)) { 1766 warnedKeys.add(key); 1767 this.logger.warn(message, object); 1768 } 1769 } 1770} 1771 1772function attributeValueContainsToken(attributeName, token) { 1773 return `[${attributeName}~="${token}"]`; 1774} 1775 1776class TargetSet { 1777 constructor(scope) { 1778 this.scope = scope; 1779 } 1780 get element() { 1781 return this.scope.element; 1782 } 1783 get identifier() { 1784 return this.scope.identifier; 1785 } 1786 get schema() { 1787 return this.scope.schema; 1788 } 1789 has(targetName) { 1790 return this.find(targetName) != null; 1791 } 1792 find(...targetNames) { 1793 return targetNames.reduce((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), undefined); 1794 } 1795 findAll(...targetNames) { 1796 return targetNames.reduce((targets, targetName) => [ 1797 ...targets, 1798 ...this.findAllTargets(targetName), 1799 ...this.findAllLegacyTargets(targetName), 1800 ], []); 1801 } 1802 findTarget(targetName) { 1803 const selector = this.getSelectorForTargetName(targetName); 1804 return this.scope.findElement(selector); 1805 } 1806 findAllTargets(targetName) { 1807 const selector = this.getSelectorForTargetName(targetName); 1808 return this.scope.findAllElements(selector); 1809 } 1810 getSelectorForTargetName(targetName) { 1811 const attributeName = this.schema.targetAttributeForScope(this.identifier); 1812 return attributeValueContainsToken(attributeName, targetName); 1813 } 1814 findLegacyTarget(targetName) { 1815 const selector = this.getLegacySelectorForTargetName(targetName); 1816 return this.deprecate(this.scope.findElement(selector), targetName); 1817 } 1818 findAllLegacyTargets(targetName) { 1819 const selector = this.getLegacySelectorForTargetName(targetName); 1820 return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName)); 1821 } 1822 getLegacySelectorForTargetName(targetName) { 1823 const targetDescriptor = `${this.identifier}.${targetName}`; 1824 return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor); 1825 } 1826 deprecate(element, targetName) { 1827 if (element) { 1828 const { identifier } = this; 1829 const attributeName = this.schema.targetAttribute; 1830 const revisedAttributeName = this.schema.targetAttributeForScope(identifier); 1831 this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". ` + 1832 `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`); 1833 } 1834 return element; 1835 } 1836 get guide() { 1837 return this.scope.guide; 1838 } 1839} 1840 1841class OutletSet { 1842 constructor(scope, controllerElement) { 1843 this.scope = scope; 1844 this.controllerElement = controllerElement; 1845 } 1846 get element() { 1847 return this.scope.element; 1848 } 1849 get identifier() { 1850 return this.scope.identifier; 1851 } 1852 get schema() { 1853 return this.scope.schema; 1854 } 1855 has(outletName) { 1856 return this.find(outletName) != null; 1857 } 1858 find(...outletNames) { 1859 return outletNames.reduce((outlet, outletName) => outlet || this.findOutlet(outletName), undefined); 1860 } 1861 findAll(...outletNames) { 1862 return outletNames.reduce((outlets, outletName) => [...outlets, ...this.findAllOutlets(outletName)], []); 1863 } 1864 getSelectorForOutletName(outletName) { 1865 const attributeName = this.schema.outletAttributeForScope(this.identifier, outletName); 1866 return this.controllerElement.getAttribute(attributeName); 1867 } 1868 findOutlet(outletName) { 1869 const selector = this.getSelectorForOutletName(outletName); 1870 if (selector) 1871 return this.findElement(selector, outletName); 1872 } 1873 findAllOutlets(outletName) { 1874 const selector = this.getSelectorForOutletName(outletName); 1875 return selector ? this.findAllElements(selector, outletName) : []; 1876 } 1877 findElement(selector, outletName) { 1878 const elements = this.scope.queryElements(selector); 1879 return elements.filter((element) => this.matchesElement(element, selector, outletName))[0]; 1880 } 1881 findAllElements(selector, outletName) { 1882 const elements = this.scope.queryElements(selector); 1883 return elements.filter((element) => this.matchesElement(element, selector, outletName)); 1884 } 1885 matchesElement(element, selector, outletName) { 1886 const controllerAttribute = element.getAttribute(this.scope.schema.controllerAttribute) || ""; 1887 return element.matches(selector) && controllerAttribute.split(" ").includes(outletName); 1888 } 1889} 1890 1891class Scope { 1892 constructor(schema, element, identifier, logger) { 1893 this.targets = new TargetSet(this); 1894 this.classes = new ClassMap(this); 1895 this.data = new DataMap(this); 1896 this.containsElement = (element) => { 1897 return element.closest(this.controllerSelector) === this.element; 1898 }; 1899 this.schema = schema; 1900 this.element = element; 1901 this.identifier = identifier; 1902 this.guide = new Guide(logger); 1903 this.outlets = new OutletSet(this.documentScope, element); 1904 } 1905 findElement(selector) { 1906 return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement); 1907 } 1908 findAllElements(selector) { 1909 return [ 1910 ...(this.element.matches(selector) ? [this.element] : []), 1911 ...this.queryElements(selector).filter(this.containsElement), 1912 ]; 1913 } 1914 queryElements(selector) { 1915 return Array.from(this.element.querySelectorAll(selector)); 1916 } 1917 get controllerSelector() { 1918 return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier); 1919 } 1920 get isDocumentScope() { 1921 return this.element === document.documentElement; 1922 } 1923 get documentScope() { 1924 return this.isDocumentScope 1925 ? this 1926 : new Scope(this.schema, document.documentElement, this.identifier, this.guide.logger); 1927 } 1928} 1929 1930class ScopeObserver { 1931 constructor(element, schema, delegate) { 1932 this.element = element; 1933 this.schema = schema; 1934 this.delegate = delegate; 1935 this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this); 1936 this.scopesByIdentifierByElement = new WeakMap(); 1937 this.scopeReferenceCounts = new WeakMap(); 1938 } 1939 start() { 1940 this.valueListObserver.start(); 1941 } 1942 stop() { 1943 this.valueListObserver.stop(); 1944 } 1945 get controllerAttribute() { 1946 return this.schema.controllerAttribute; 1947 } 1948 parseValueForToken(token) { 1949 const { element, content: identifier } = token; 1950 return this.parseValueForElementAndIdentifier(element, identifier); 1951 } 1952 parseValueForElementAndIdentifier(element, identifier) { 1953 const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element); 1954 let scope = scopesByIdentifier.get(identifier); 1955 if (!scope) { 1956 scope = this.delegate.createScopeForElementAndIdentifier(element, identifier); 1957 scopesByIdentifier.set(identifier, scope); 1958 } 1959 return scope; 1960 } 1961 elementMatchedValue(element, value) { 1962 const referenceCount = (this.scopeReferenceCounts.get(value) || 0) + 1; 1963 this.scopeReferenceCounts.set(value, referenceCount); 1964 if (referenceCount == 1) { 1965 this.delegate.scopeConnected(value); 1966 } 1967 } 1968 elementUnmatchedValue(element, value) { 1969 const referenceCount = this.scopeReferenceCounts.get(value); 1970 if (referenceCount) { 1971 this.scopeReferenceCounts.set(value, referenceCount - 1); 1972 if (referenceCount == 1) { 1973 this.delegate.scopeDisconnected(value); 1974 } 1975 } 1976 } 1977 fetchScopesByIdentifierForElement(element) { 1978 let scopesByIdentifier = this.scopesByIdentifierByElement.get(element); 1979 if (!scopesByIdentifier) { 1980 scopesByIdentifier = new Map(); 1981 this.scopesByIdentifierByElement.set(element, scopesByIdentifier); 1982 } 1983 return scopesByIdentifier; 1984 } 1985} 1986 1987class Router { 1988 constructor(application) { 1989 this.application = application; 1990 this.scopeObserver = new ScopeObserver(this.element, this.schema, this); 1991 this.scopesByIdentifier = new Multimap(); 1992 this.modulesByIdentifier = new Map(); 1993 } 1994 get element() { 1995 return this.application.element; 1996 } 1997 get schema() { 1998 return this.application.schema; 1999 } 2000 get logger() { 2001 return this.application.logger; 2002 } 2003 get controllerAttribute() { 2004 return this.schema.controllerAttribute; 2005 } 2006 get modules() { 2007 return Array.from(this.modulesByIdentifier.values()); 2008 } 2009 get contexts() { 2010 return this.modules.reduce((contexts, module) => contexts.concat(module.contexts), []); 2011 } 2012 start() { 2013 this.scopeObserver.start(); 2014 } 2015 stop() { 2016 this.scopeObserver.stop(); 2017 } 2018 loadDefinition(definition) { 2019 this.unloadIdentifier(definition.identifier); 2020 const module = new Module(this.application, definition); 2021 this.connectModule(module); 2022 const afterLoad = definition.controllerConstructor.afterLoad; 2023 if (afterLoad) { 2024 afterLoad.call(definition.controllerConstructor, definition.identifier, this.application); 2025 } 2026 } 2027 unloadIdentifier(identifier) { 2028 const module = this.modulesByIdentifier.get(identifier); 2029 if (module) { 2030 this.disconnectModule(module); 2031 } 2032 } 2033 getContextForElementAndIdentifier(element, identifier) { 2034 const module = this.modulesByIdentifier.get(identifier); 2035 if (module) { 2036 return module.contexts.find((context) => context.element == element); 2037 } 2038 } 2039 proposeToConnectScopeForElementAndIdentifier(element, identifier) { 2040 const scope = this.scopeObserver.parseValueForElementAndIdentifier(element, identifier); 2041 if (scope) { 2042 this.scopeObserver.elementMatchedValue(scope.element, scope); 2043 } 2044 else { 2045 console.error(`Couldn't find or create scope for identifier: "${identifier}" and element:`, element); 2046 } 2047 } 2048 handleError(error, message, detail) { 2049 this.application.handleError(error, message, detail); 2050 } 2051 createScopeForElementAndIdentifier(element, identifier) { 2052 return new Scope(this.schema, element, identifier, this.logger); 2053 } 2054 scopeConnected(scope) { 2055 this.scopesByIdentifier.add(scope.identifier, scope); 2056 const module = this.modulesByIdentifier.get(scope.identifier); 2057 if (module) { 2058 module.connectContextForScope(scope); 2059 } 2060 } 2061 scopeDisconnected(scope) { 2062 this.scopesByIdentifier.delete(scope.identifier, scope); 2063 const module = this.modulesByIdentifier.get(scope.identifier); 2064 if (module) { 2065 module.disconnectContextForScope(scope); 2066 } 2067 } 2068 connectModule(module) { 2069 this.modulesByIdentifier.set(module.identifier, module); 2070 const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier); 2071 scopes.forEach((scope) => module.connectContextForScope(scope)); 2072 } 2073 disconnectModule(module) { 2074 this.modulesByIdentifier.delete(module.identifier); 2075 const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier); 2076 scopes.forEach((scope) => module.disconnectContextForScope(scope)); 2077 } 2078} 2079 2080const defaultSchema = { 2081 controllerAttribute: "data-controller", 2082 actionAttribute: "data-action", 2083 targetAttribute: "data-target", 2084 targetAttributeForScope: (identifier) => `data-${identifier}-target`, 2085 outletAttributeForScope: (identifier, outlet) => `data-${identifier}-${outlet}-outlet`, 2086 keyMappings: Object.assign(Object.assign({ enter: "Enter", tab: "Tab", esc: "Escape", space: " ", up: "ArrowUp", down: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", home: "Home", end: "End", page_up: "PageUp", page_down: "PageDown" }, objectFromEntries("abcdefghijklmnopqrstuvwxyz".split("").map((c) => [c, c]))), objectFromEntries("0123456789".split("").map((n) => [n, n]))), 2087}; 2088function objectFromEntries(array) { 2089 return array.reduce((memo, [k, v]) => (Object.assign(Object.assign({}, memo), { [k]: v })), {}); 2090} 2091 2092class Application { 2093 constructor(element = document.documentElement, schema = defaultSchema) { 2094 this.logger = console; 2095 this.debug = false; 2096 this.logDebugActivity = (identifier, functionName, detail = {}) => { 2097 if (this.debug) { 2098 this.logFormattedMessage(identifier, functionName, detail); 2099 } 2100 }; 2101 this.element = element; 2102 this.schema = schema; 2103 this.dispatcher = new Dispatcher(this); 2104 this.router = new Router(this); 2105 this.actionDescriptorFilters = Object.assign({}, defaultActionDescriptorFilters); 2106 } 2107 static start(element, schema) { 2108 const application = new this(element, schema); 2109 application.start(); 2110 return application; 2111 } 2112 async start() { 2113 await domReady(); 2114 this.logDebugActivity("application", "starting"); 2115 this.dispatcher.start(); 2116 this.router.start(); 2117 this.logDebugActivity("application", "start"); 2118 } 2119 stop() { 2120 this.logDebugActivity("application", "stopping"); 2121 this.dispatcher.stop(); 2122 this.router.stop(); 2123 this.logDebugActivity("application", "stop"); 2124 } 2125 register(identifier, controllerConstructor) { 2126 this.load({ identifier, controllerConstructor }); 2127 } 2128 registerActionOption(name, filter) { 2129 this.actionDescriptorFilters[name] = filter; 2130 } 2131 load(head, ...rest) { 2132 const definitions = Array.isArray(head) ? head : [head, ...rest]; 2133 definitions.forEach((definition) => { 2134 if (definition.controllerConstructor.shouldLoad) { 2135 this.router.loadDefinition(definition); 2136 } 2137 }); 2138 } 2139 unload(head, ...rest) { 2140 const identifiers = Array.isArray(head) ? head : [head, ...rest]; 2141 identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier)); 2142 } 2143 get controllers() { 2144 return this.router.contexts.map((context) => context.controller); 2145 } 2146 getControllerForElementAndIdentifier(element, identifier) { 2147 const context = this.router.getContextForElementAndIdentifier(element, identifier); 2148 return context ? context.controller : null; 2149 } 2150 handleError(error, message, detail) { 2151 var _a; 2152 this.logger.error(`%s\n\n%o\n\n%o`, message, error, detail); 2153 (_a = window.onerror) === null || _a === void 0 ? void 0 : _a.call(window, message, "", 0, 0, error); 2154 } 2155 logFormattedMessage(identifier, functionName, detail = {}) { 2156 detail = Object.assign({ application: this }, detail); 2157 this.logger.groupCollapsed(`${identifier} #${functionName}`); 2158 this.logger.log("details:", Object.assign({}, detail)); 2159 this.logger.groupEnd(); 2160 } 2161} 2162function domReady() { 2163 return new Promise((resolve) => { 2164 if (document.readyState == "loading") { 2165 document.addEventListener("DOMContentLoaded", () => resolve()); 2166 } 2167 else { 2168 resolve(); 2169 } 2170 }); 2171} 2172 2173function ClassPropertiesBlessing(constructor) { 2174 const classes = readInheritableStaticArrayValues(constructor, "classes"); 2175 return classes.reduce((properties, classDefinition) => { 2176 return Object.assign(properties, propertiesForClassDefinition(classDefinition)); 2177 }, {}); 2178} 2179function propertiesForClassDefinition(key) { 2180 return { 2181 [`${key}Class`]: { 2182 get() { 2183 const { classes } = this; 2184 if (classes.has(key)) { 2185 return classes.get(key); 2186 } 2187 else { 2188 const attribute = classes.getAttributeName(key); 2189 throw new Error(`Missing attribute "${attribute}"`); 2190 } 2191 }, 2192 }, 2193 [`${key}Classes`]: { 2194 get() { 2195 return this.classes.getAll(key); 2196 }, 2197 }, 2198 [`has${capitalize(key)}Class`]: { 2199 get() { 2200 return this.classes.has(key); 2201 }, 2202 }, 2203 }; 2204} 2205 2206function OutletPropertiesBlessing(constructor) { 2207 const outlets = readInheritableStaticArrayValues(constructor, "outlets"); 2208 return outlets.reduce((properties, outletDefinition) => { 2209 return Object.assign(properties, propertiesForOutletDefinition(outletDefinition)); 2210 }, {}); 2211} 2212function getOutletController(controller, element, identifier) { 2213 return controller.application.getControllerForElementAndIdentifier(element, identifier); 2214} 2215function getControllerAndEnsureConnectedScope(controller, element, outletName) { 2216 let outletController = getOutletController(controller, element, outletName); 2217 if (outletController) 2218 return outletController; 2219 controller.application.router.proposeToConnectScopeForElementAndIdentifier(element, outletName); 2220 outletController = getOutletController(controller, element, outletName); 2221 if (outletController) 2222 return outletController; 2223} 2224function propertiesForOutletDefinition(name) { 2225 const camelizedName = namespaceCamelize(name); 2226 return { 2227 [`${camelizedName}Outlet`]: { 2228 get() { 2229 const outletElement = this.outlets.find(name); 2230 const selector = this.outlets.getSelectorForOutletName(name); 2231 if (outletElement) { 2232 const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name); 2233 if (outletController) 2234 return outletController; 2235 throw new Error(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`); 2236 } 2237 throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`); 2238 }, 2239 }, 2240 [`${camelizedName}Outlets`]: { 2241 get() { 2242 const outlets = this.outlets.findAll(name); 2243 if (outlets.length > 0) { 2244 return outlets 2245 .map((outletElement) => { 2246 const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name); 2247 if (outletController) 2248 return outletController; 2249 console.warn(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`, outletElement); 2250 }) 2251 .filter((controller) => controller); 2252 } 2253 return []; 2254 }, 2255 }, 2256 [`${camelizedName}OutletElement`]: { 2257 get() { 2258 const outletElement = this.outlets.find(name); 2259 const selector = this.outlets.getSelectorForOutletName(name); 2260 if (outletElement) { 2261 return outletElement; 2262 } 2263 else { 2264 throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`); 2265 } 2266 }, 2267 }, 2268 [`${camelizedName}OutletElements`]: { 2269 get() { 2270 return this.outlets.findAll(name); 2271 }, 2272 }, 2273 [`has${capitalize(camelizedName)}Outlet`]: { 2274 get() { 2275 return this.outlets.has(name); 2276 }, 2277 }, 2278 }; 2279} 2280 2281function TargetPropertiesBlessing(constructor) { 2282 const targets = readInheritableStaticArrayValues(constructor, "targets"); 2283 return targets.reduce((properties, targetDefinition) => { 2284 return Object.assign(properties, propertiesForTargetDefinition(targetDefinition)); 2285 }, {}); 2286} 2287function propertiesForTargetDefinition(name) { 2288 return { 2289 [`${name}Target`]: { 2290 get() { 2291 const target = this.targets.find(name); 2292 if (target) { 2293 return target; 2294 } 2295 else { 2296 throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`); 2297 } 2298 }, 2299 }, 2300 [`${name}Targets`]: { 2301 get() { 2302 return this.targets.findAll(name); 2303 }, 2304 }, 2305 [`has${capitalize(name)}Target`]: { 2306 get() { 2307 return this.targets.has(name); 2308 }, 2309 }, 2310 }; 2311} 2312 2313function ValuePropertiesBlessing(constructor) { 2314 const valueDefinitionPairs = readInheritableStaticObjectPairs(constructor, "values"); 2315 const propertyDescriptorMap = { 2316 valueDescriptorMap: { 2317 get() { 2318 return valueDefinitionPairs.reduce((result, valueDefinitionPair) => { 2319 const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair, this.identifier); 2320 const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key); 2321 return Object.assign(result, { [attributeName]: valueDescriptor }); 2322 }, {}); 2323 }, 2324 }, 2325 }; 2326 return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => { 2327 return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair)); 2328 }, propertyDescriptorMap); 2329} 2330function propertiesForValueDefinitionPair(valueDefinitionPair, controller) { 2331 const definition = parseValueDefinitionPair(valueDefinitionPair, controller); 2332 const { key, name, reader: read, writer: write } = definition; 2333 return { 2334 [name]: { 2335 get() { 2336 const value = this.data.get(key); 2337 if (value !== null) { 2338 return read(value); 2339 } 2340 else { 2341 return definition.defaultValue; 2342 } 2343 }, 2344 set(value) { 2345 if (value === undefined) { 2346 this.data.delete(key); 2347 } 2348 else { 2349 this.data.set(key, write(value)); 2350 } 2351 }, 2352 }, 2353 [`has${capitalize(name)}`]: { 2354 get() { 2355 return this.data.has(key) || definition.hasCustomDefaultValue; 2356 }, 2357 }, 2358 }; 2359} 2360function parseValueDefinitionPair([token, typeDefinition], controller) { 2361 return valueDescriptorForTokenAndTypeDefinition({ 2362 controller, 2363 token, 2364 typeDefinition, 2365 }); 2366} 2367function parseValueTypeConstant(constant) { 2368 switch (constant) { 2369 case Array: 2370 return "array"; 2371 case Boolean: 2372 return "boolean"; 2373 case Number: 2374 return "number"; 2375 case Object: 2376 return "object"; 2377 case String: 2378 return "string"; 2379 } 2380} 2381function parseValueTypeDefault(defaultValue) { 2382 switch (typeof defaultValue) { 2383 case "boolean": 2384 return "boolean"; 2385 case "number": 2386 return "number"; 2387 case "string": 2388 return "string"; 2389 } 2390 if (Array.isArray(defaultValue)) 2391 return "array"; 2392 if (Object.prototype.toString.call(defaultValue) === "[object Object]") 2393 return "object"; 2394} 2395function parseValueTypeObject(payload) { 2396 const { controller, token, typeObject } = payload; 2397 const hasType = isSomething(typeObject.type); 2398 const hasDefault = isSomething(typeObject.default); 2399 const fullObject = hasType && hasDefault; 2400 const onlyType = hasType && !hasDefault; 2401 const onlyDefault = !hasType && hasDefault; 2402 const typeFromObject = parseValueTypeConstant(typeObject.type); 2403 const typeFromDefaultValue = parseValueTypeDefault(payload.typeObject.default); 2404 if (onlyType) 2405 return typeFromObject; 2406 if (onlyDefault) 2407 return typeFromDefaultValue; 2408 if (typeFromObject !== typeFromDefaultValue) { 2409 const propertyPath = controller ? `${controller}.${token}` : token; 2410 throw new Error(`The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${typeObject.default}" is of type "${typeFromDefaultValue}".`); 2411 } 2412 if (fullObject) 2413 return typeFromObject; 2414} 2415function parseValueTypeDefinition(payload) { 2416 const { controller, token, typeDefinition } = payload; 2417 const typeObject = { controller, token, typeObject: typeDefinition }; 2418 const typeFromObject = parseValueTypeObject(typeObject); 2419 const typeFromDefaultValue = parseValueTypeDefault(typeDefinition); 2420 const typeFromConstant = parseValueTypeConstant(typeDefinition); 2421 const type = typeFromObject || typeFromDefaultValue || typeFromConstant; 2422 if (type) 2423 return type; 2424 const propertyPath = controller ? `${controller}.${typeDefinition}` : token; 2425 throw new Error(`Unknown value type "${propertyPath}" for "${token}" value`); 2426} 2427function defaultValueForDefinition(typeDefinition) { 2428 const constant = parseValueTypeConstant(typeDefinition); 2429 if (constant) 2430 return defaultValuesByType[constant]; 2431 const hasDefault = hasProperty(typeDefinition, "default"); 2432 const hasType = hasProperty(typeDefinition, "type"); 2433 const typeObject = typeDefinition; 2434 if (hasDefault) 2435 return typeObject.default; 2436 if (hasType) { 2437 const { type } = typeObject; 2438 const constantFromType = parseValueTypeConstant(type); 2439 if (constantFromType) 2440 return defaultValuesByType[constantFromType]; 2441 } 2442 return typeDefinition; 2443} 2444function valueDescriptorForTokenAndTypeDefinition(payload) { 2445 const { token, typeDefinition } = payload; 2446 const key = `${dasherize(token)}-value`; 2447 const type = parseValueTypeDefinition(payload); 2448 return { 2449 type, 2450 key, 2451 name: camelize(key), 2452 get defaultValue() { 2453 return defaultValueForDefinition(typeDefinition); 2454 }, 2455 get hasCustomDefaultValue() { 2456 return parseValueTypeDefault(typeDefinition) !== undefined; 2457 }, 2458 reader: readers[type], 2459 writer: writers[type] || writers.default, 2460 }; 2461} 2462const defaultValuesByType = { 2463 get array() { 2464 return []; 2465 }, 2466 boolean: false, 2467 number: 0, 2468 get object() { 2469 return {}; 2470 }, 2471 string: "", 2472}; 2473const readers = { 2474 array(value) { 2475 const array = JSON.parse(value); 2476 if (!Array.isArray(array)) { 2477 throw new TypeError(`expected value of type "array" but instead got value "${value}" of type "${parseValueTypeDefault(array)}"`); 2478 } 2479 return array; 2480 }, 2481 boolean(value) { 2482 return !(value == "0" || String(value).toLowerCase() == "false"); 2483 }, 2484 number(value) { 2485 return Number(value.replace(/_/g, "")); 2486 }, 2487 object(value) { 2488 const object = JSON.parse(value); 2489 if (object === null || typeof object != "object" || Array.isArray(object)) { 2490 throw new TypeError(`expected value of type "object" but instead got value "${value}" of type "${parseValueTypeDefault(object)}"`); 2491 } 2492 return object; 2493 }, 2494 string(value) { 2495 return value; 2496 }, 2497}; 2498const writers = { 2499 default: writeString, 2500 array: writeJSON, 2501 object: writeJSON, 2502}; 2503function writeJSON(value) { 2504 return JSON.stringify(value); 2505} 2506function writeString(value) { 2507 return `${value}`; 2508} 2509 2510class Controller { 2511 constructor(context) { 2512 this.context = context; 2513 } 2514 static get shouldLoad() { 2515 return true; 2516 } 2517 static afterLoad(_identifier, _application) { 2518 return; 2519 } 2520 get application() { 2521 return this.context.application; 2522 } 2523 get scope() { 2524 return this.context.scope; 2525 } 2526 get element() { 2527 return this.scope.element; 2528 } 2529 get identifier() { 2530 return this.scope.identifier; 2531 } 2532 get targets() { 2533 return this.scope.targets; 2534 } 2535 get outlets() { 2536 return this.scope.outlets; 2537 } 2538 get classes() { 2539 return this.scope.classes; 2540 } 2541 get data() { 2542 return this.scope.data; 2543 } 2544 initialize() { 2545 } 2546 connect() { 2547 } 2548 disconnect() { 2549 } 2550 dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true, } = {}) { 2551 const type = prefix ? `${prefix}:${eventName}` : eventName; 2552 const event = new CustomEvent(type, { detail, bubbles, cancelable }); 2553 target.dispatchEvent(event); 2554 return event; 2555 } 2556} 2557Controller.blessings = [ 2558 ClassPropertiesBlessing, 2559 TargetPropertiesBlessing, 2560 ValuePropertiesBlessing, 2561 OutletPropertiesBlessing, 2562]; 2563Controller.targets = []; 2564Controller.outlets = []; 2565Controller.values = {}; 2566 2567export { Application, AttributeObserver, Context, Controller, ElementObserver, IndexedMultimap, Multimap, SelectorObserver, StringMapObserver, TokenListObserver, ValueListObserver, add, defaultSchema, del, fetch, prune };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.