vendor: 4,247 bytes, lines 1-123
1//= link ./stimulus-autoloader.js 2//= link ./stimulus-importmap-autoloader.js 3//= link ./stimulus-loading.js 4 5/* 6Stimulus 3.0.1 7Copyright © 2021 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 get bindings() { 40 return Array.from(this.unorderedBindings).sort((left, right) => { 41 const leftIndex = left.index, rightIndex = right.index; 42 return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0; 43 }); 44 } 45} 46function extendEvent(event) { 47 if ("immediatePropagationStopped" in event) { 48 return event; 49 } 50 else { 51 const { stopImmediatePropagation } = event; 52 return Object.assign(event, { 53 immediatePropagationStopped: false, 54 stopImmediatePropagation() { 55 this.immediatePropagationStopped = true; 56 stopImmediatePropagation.call(this); 57 } 58 }); 59 } 60} 61 62class Dispatcher { 63 constructor(application) { 64 this.application = application; 65 this.eventListenerMaps = new Map; 66 this.started = false; 67 } 68 start() { 69 if (!this.started) { 70 this.started = true; 71 this.eventListeners.forEach(eventListener => eventListener.connect()); 72 } 73 } 74 stop() { 75 if (this.started) { 76 this.started = false; 77 this.eventListeners.forEach(eventListener => eventListener.disconnect()); 78 } 79 } 80 get eventListeners() { 81 return Array.from(this.eventListenerMaps.values()) 82 .reduce((listeners, map) => listeners.concat(Array.from(map.values())), []); 83 } 84 bindingConnected(binding) { 85 this.fetchEventListenerForBinding(binding).bindingConnected(binding); 86 } 87 bindingDisconnected(binding) { 88 this.fetchEventListenerForBinding(binding).bindingDisconnected(binding); 89 } 90 handleError(error, message, detail = {}) { 91 this.application.handleError(error, `Error ${message}`, detail); 92 } 93 fetchEventListenerForBinding(binding) { 94 const { eventTarget, eventName, eventOptions } = binding; 95 return this.fetchEventListener(eventTarget, eventName, eventOptions); 96 } 97 fetchEventListener(eventTarget, eventName, eventOptions) { 98 const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget); 99 const cacheKey = this.cacheKey(eventName, eventOptions); 100 let eventListener = eventListenerMap.get(cacheKey); 101 if (!eventListener) { 102 eventListener = this.createEventListener(eventTarget, eventName, eventOptions); 103 eventListenerMap.set(cacheKey, eventListener); 104 } 105 return eventListener; 106 } 107 createEventListener(eventTarget, eventName, eventOptions) { 108 const eventListener = new EventListener(eventTarget, eventName, eventOptions); 109 if (this.started) { 110 eventListener.connect(); 111 } 112 return eventListener; 113 } 114 fetchEventListenerMapForEventTarget(eventTarget) { 115 let eventListenerMap = this.eventListenerMaps.get(eventTarget); 116 if (!eventListenerMap) { 117 eventListenerMap = new Map; 118 this.eventListenerMaps.set(eventTarget, eventListenerMap); 119 } 120 return eventListenerMap; 121 } 122 cacheKey(eventName, eventOptions) { 123 const parts = [eventName];
124 Object.keys(eventOptions).sort().forEach(key => { 125 parts.push(`${eventOptions[key] ? "" : "!"}${key}`); 126 }); 127 return parts.join(":"); 128 } 129} 130 131const descriptorPattern = /^((.+?)(@(window|document))?->)?(.+?)(#([^:]+?))(:(.+))?$/; 132function parseActionDescriptorString(descriptorString) { 133 const source = descriptorString.trim(); 134 const matches = source.match(descriptorPattern) || []; 135 return { 136 eventTarget: parseEventTarget(matches[4]), 137 eventName: matches[2], 138 eventOptions: matches[9] ? parseEventOptions(matches[9]) : {}, 139 identifier: matches[5], 140 methodName: matches[7] 141 }; 142} 143function parseEventTarget(eventTargetName) { 144 if (eventTargetName == "window") { 145 return window; 146 } 147 else if (eventTargetName == "document") { 148 return document; 149 } 150} 151function parseEventOptions(eventOptions) { 152 return eventOptions.split(":").reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {}); 153} 154function stringifyEventTarget(eventTarget) { 155 if (eventTarget == window) { 156 return "window"; 157 } 158 else if (eventTarget == document) { 159 return "document"; 160 } 161} 162 163function camelize(value) { 164 return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase()); 165} 166function capitalize(value) { 167 return value.charAt(0).toUpperCase() + value.slice(1); 168} 169function dasherize(value) { 170 return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`); 171} 172function tokenize(value) { 173 return value.match(/[^\s]+/g) || []; 174} 175 176class Action { 177 constructor(element, index, descriptor) { 178 this.element = element; 179 this.index = index; 180 this.eventTarget = descriptor.eventTarget || element; 181 this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name"); 182 this.eventOptions = descriptor.eventOptions || {}; 183 this.identifier = descriptor.identifier || error("missing identifier"); 184 this.methodName = descriptor.methodName || error("missing method name"); 185 } 186 static forToken(token) { 187 return new this(token.element, token.index, parseActionDescriptorString(token.content)); 188 } 189 toString() { 190 const eventNameSuffix = this.eventTargetName ? `@${this.eventTargetName}` : ""; 191 return `${this.eventName}${eventNameSuffix}->${this.identifier}#${this.methodName}`; 192 } 193 get params() { 194 if (this.eventTarget instanceof Element) { 195 return this.getParamsFromEventTargetAttributes(this.eventTarget); 196 } 197 else { 198 return {}; 199 } 200 } 201 getParamsFromEventTargetAttributes(eventTarget) { 202 const params = {}; 203 const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`); 204 const attributes = Array.from(eventTarget.attributes); 205 attributes.forEach(({ name, value }) => { 206 const match = name.match(pattern); 207 const key = match && match[1]; 208 if (key) { 209 Object.assign(params, { [camelize(key)]: typecast(value) }); 210 } 211 }); 212 return params; 213 } 214 get eventTargetName() { 215 return stringifyEventTarget(this.eventTarget); 216 } 217} 218const defaultEventNames = { 219 "a": e => "click", 220 "button": e => "click", 221 "form": e => "submit", 222 "details": e => "toggle", 223 "input": e => e.getAttribute("type") == "submit" ? "click" : "input", 224 "select": e => "change", 225 "textarea": e => "input" 226}; 227function getDefaultEventNameForElement(element) { 228 const tagName = element.tagName.toLowerCase(); 229 if (tagName in defaultEventNames) { 230 return defaultEventNames[tagName](element); 231 } 232} 233function error(message) { 234 throw new Error(message); 235} 236function typecast(value) { 237 try { 238 return JSON.parse(value); 239 } 240 catch (o_O) { 241 return value; 242 } 243} 244 245class Binding { 246 constructor(context, action) { 247 this.context = context; 248 this.action = action; 249 } 250 get index() { 251 return this.action.index; 252 } 253 get eventTarget() { 254 return this.action.eventTarget; 255 } 256 get eventOptions() { 257 return this.action.eventOptions; 258 } 259 get identifier() {
vendor: 50,148 bytes, lines 260-1766
260 return this.context.identifier; 261 } 262 handleEvent(event) { 263 if (this.willBeInvokedByEvent(event)) { 264 this.invokeWithEvent(event); 265 } 266 } 267 get eventName() { 268 return this.action.eventName; 269 } 270 get method() { 271 const method = this.controller[this.methodName]; 272 if (typeof method == "function") { 273 return method; 274 } 275 throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`); 276 } 277 invokeWithEvent(event) { 278 const { target, currentTarget } = event; 279 try { 280 const { params } = this.action; 281 const actionEvent = Object.assign(event, { params }); 282 this.method.call(this.controller, actionEvent); 283 this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName }); 284 } 285 catch (error) { 286 const { identifier, controller, element, index } = this; 287 const detail = { identifier, controller, element, index, event }; 288 this.context.handleError(error, `invoking action "${this.action}"`, detail); 289 } 290 } 291 willBeInvokedByEvent(event) { 292 const eventTarget = event.target; 293 if (this.element === eventTarget) { 294 return true; 295 } 296 else if (eventTarget instanceof Element && this.element.contains(eventTarget)) { 297 return this.scope.containsElement(eventTarget); 298 } 299 else { 300 return this.scope.containsElement(this.action.element); 301 } 302 } 303 get controller() { 304 return this.context.controller; 305 } 306 get methodName() { 307 return this.action.methodName; 308 } 309 get element() { 310 return this.scope.element; 311 } 312 get scope() { 313 return this.context.scope; 314 } 315} 316 317class ElementObserver { 318 constructor(element, delegate) { 319 this.mutationObserverInit = { attributes: true, childList: true, subtree: true }; 320 this.element = element; 321 this.started = false; 322 this.delegate = delegate; 323 this.elements = new Set; 324 this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations)); 325 } 326 start() { 327 if (!this.started) { 328 this.started = true; 329 this.mutationObserver.observe(this.element, this.mutationObserverInit); 330 this.refresh(); 331 } 332 } 333 pause(callback) { 334 if (this.started) { 335 this.mutationObserver.disconnect(); 336 this.started = false; 337 } 338 callback(); 339 if (!this.started) { 340 this.mutationObserver.observe(this.element, this.mutationObserverInit); 341 this.started = true; 342 } 343 } 344 stop() { 345 if (this.started) { 346 this.mutationObserver.takeRecords(); 347 this.mutationObserver.disconnect(); 348 this.started = false; 349 } 350 } 351 refresh() { 352 if (this.started) { 353 const matches = new Set(this.matchElementsInTree()); 354 for (const element of Array.from(this.elements)) { 355 if (!matches.has(element)) { 356 this.removeElement(element); 357 } 358 } 359 for (const element of Array.from(matches)) { 360 this.addElement(element); 361 } 362 } 363 } 364 processMutations(mutations) { 365 if (this.started) { 366 for (const mutation of mutations) { 367 this.processMutation(mutation); 368 } 369 } 370 } 371 processMutation(mutation) { 372 if (mutation.type == "attributes") { 373 this.processAttributeChange(mutation.target, mutation.attributeName); 374 } 375 else if (mutation.type == "childList") { 376 this.processRemovedNodes(mutation.removedNodes); 377 this.processAddedNodes(mutation.addedNodes); 378 } 379 } 380 processAttributeChange(node, attributeName) { 381 const element = node; 382 if (this.elements.has(element)) { 383 if (this.delegate.elementAttributeChanged && this.matchElement(element)) { 384 this.delegate.elementAttributeChanged(element, attributeName); 385 } 386 else { 387 this.removeElement(element); 388 } 389 } 390 else if (this.matchElement(element)) { 391 this.addElement(element); 392 } 393 } 394 processRemovedNodes(nodes) { 395 for (const node of Array.from(nodes)) { 396 const element = this.elementFromNode(node); 397 if (element) { 398 this.processTree(element, this.removeElement); 399 } 400 } 401 } 402 processAddedNodes(nodes) { 403 for (const node of Array.from(nodes)) { 404 const element = this.elementFromNode(node); 405 if (element && this.elementIsActive(element)) { 406 this.processTree(element, this.addElement); 407 } 408 } 409 } 410 matchElement(element) { 411 return this.delegate.matchElement(element); 412 } 413 matchElementsInTree(tree = this.element) { 414 return this.delegate.matchElementsInTree(tree); 415 } 416 processTree(tree, processor) { 417 for (const element of this.matchElementsInTree(tree)) { 418 processor.call(this, element); 419 } 420 } 421 elementFromNode(node) { 422 if (node.nodeType == Node.ELEMENT_NODE) { 423 return node; 424 } 425 } 426 elementIsActive(element) { 427 if (element.isConnected != this.element.isConnected) { 428 return false; 429 } 430 else { 431 return this.element.contains(element); 432 } 433 } 434 addElement(element) { 435 if (!this.elements.has(element)) { 436 if (this.elementIsActive(element)) { 437 this.elements.add(element); 438 if (this.delegate.elementMatched) { 439 this.delegate.elementMatched(element); 440 } 441 } 442 } 443 } 444 removeElement(element) { 445 if (this.elements.has(element)) { 446 this.elements.delete(element); 447 if (this.delegate.elementUnmatched) { 448 this.delegate.elementUnmatched(element); 449 } 450 } 451 } 452} 453 454class AttributeObserver { 455 constructor(element, attributeName, delegate) { 456 this.attributeName = attributeName; 457 this.delegate = delegate; 458 this.elementObserver = new ElementObserver(element, this); 459 } 460 get element() { 461 return this.elementObserver.element; 462 } 463 get selector() { 464 return `[${this.attributeName}]`; 465 } 466 start() { 467 this.elementObserver.start(); 468 } 469 pause(callback) { 470 this.elementObserver.pause(callback); 471 } 472 stop() { 473 this.elementObserver.stop(); 474 } 475 refresh() { 476 this.elementObserver.refresh(); 477 } 478 get started() { 479 return this.elementObserver.started; 480 } 481 matchElement(element) { 482 return element.hasAttribute(this.attributeName); 483 } 484 matchElementsInTree(tree) { 485 const match = this.matchElement(tree) ? [tree] : []; 486 const matches = Array.from(tree.querySelectorAll(this.selector)); 487 return match.concat(matches); 488 } 489 elementMatched(element) { 490 if (this.delegate.elementMatchedAttribute) { 491 this.delegate.elementMatchedAttribute(element, this.attributeName); 492 } 493 } 494 elementUnmatched(element) { 495 if (this.delegate.elementUnmatchedAttribute) { 496 this.delegate.elementUnmatchedAttribute(element, this.attributeName); 497 } 498 } 499 elementAttributeChanged(element, attributeName) { 500 if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) { 501 this.delegate.elementAttributeValueChanged(element, attributeName); 502 } 503 } 504} 505 506class StringMapObserver { 507 constructor(element, delegate) { 508 this.element = element; 509 this.delegate = delegate; 510 this.started = false; 511 this.stringMap = new Map; 512 this.mutationObserver = new MutationObserver(mutations => this.processMutations(mutations)); 513 } 514 start() { 515 if (!this.started) { 516 this.started = true; 517 this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true }); 518 this.refresh(); 519 } 520 } 521 stop() { 522 if (this.started) { 523 this.mutationObserver.takeRecords(); 524 this.mutationObserver.disconnect(); 525 this.started = false; 526 } 527 } 528 refresh() { 529 if (this.started) { 530 for (const attributeName of this.knownAttributeNames) { 531 this.refreshAttribute(attributeName, null); 532 } 533 } 534 } 535 processMutations(mutations) { 536 if (this.started) { 537 for (const mutation of mutations) { 538 this.processMutation(mutation); 539 } 540 } 541 } 542 processMutation(mutation) { 543 const attributeName = mutation.attributeName; 544 if (attributeName) { 545 this.refreshAttribute(attributeName, mutation.oldValue); 546 } 547 } 548 refreshAttribute(attributeName, oldValue) { 549 const key = this.delegate.getStringMapKeyForAttribute(attributeName); 550 if (key != null) { 551 if (!this.stringMap.has(attributeName)) { 552 this.stringMapKeyAdded(key, attributeName); 553 } 554 const value = this.element.getAttribute(attributeName); 555 if (this.stringMap.get(attributeName) != value) { 556 this.stringMapValueChanged(value, key, oldValue); 557 } 558 if (value == null) { 559 const oldValue = this.stringMap.get(attributeName); 560 this.stringMap.delete(attributeName); 561 if (oldValue) 562 this.stringMapKeyRemoved(key, attributeName, oldValue); 563 } 564 else { 565 this.stringMap.set(attributeName, value); 566 } 567 } 568 } 569 stringMapKeyAdded(key, attributeName) { 570 if (this.delegate.stringMapKeyAdded) { 571 this.delegate.stringMapKeyAdded(key, attributeName); 572 } 573 } 574 stringMapValueChanged(value, key, oldValue) { 575 if (this.delegate.stringMapValueChanged) { 576 this.delegate.stringMapValueChanged(value, key, oldValue); 577 } 578 } 579 stringMapKeyRemoved(key, attributeName, oldValue) { 580 if (this.delegate.stringMapKeyRemoved) { 581 this.delegate.stringMapKeyRemoved(key, attributeName, oldValue); 582 } 583 } 584 get knownAttributeNames() { 585 return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames))); 586 } 587 get currentAttributeNames() { 588 return Array.from(this.element.attributes).map(attribute => attribute.name); 589 } 590 get recordedAttributeNames() { 591 return Array.from(this.stringMap.keys()); 592 } 593} 594 595function add(map, key, value) { 596 fetch(map, key).add(value); 597} 598function del(map, key, value) { 599 fetch(map, key).delete(value); 600 prune(map, key); 601} 602function fetch(map, key) { 603 let values = map.get(key); 604 if (!values) { 605 values = new Set(); 606 map.set(key, values); 607 } 608 return values; 609} 610function prune(map, key) { 611 const values = map.get(key); 612 if (values != null && values.size == 0) { 613 map.delete(key); 614 } 615} 616 617class Multimap { 618 constructor() { 619 this.valuesByKey = new Map(); 620 } 621 get keys() { 622 return Array.from(this.valuesByKey.keys()); 623 } 624 get values() { 625 const sets = Array.from(this.valuesByKey.values()); 626 return sets.reduce((values, set) => values.concat(Array.from(set)), []); 627 } 628 get size() { 629 const sets = Array.from(this.valuesByKey.values()); 630 return sets.reduce((size, set) => size + set.size, 0); 631 } 632 add(key, value) { 633 add(this.valuesByKey, key, value); 634 } 635 delete(key, value) { 636 del(this.valuesByKey, key, value); 637 } 638 has(key, value) { 639 const values = this.valuesByKey.get(key); 640 return values != null && values.has(value); 641 } 642 hasKey(key) { 643 return this.valuesByKey.has(key); 644 } 645 hasValue(value) { 646 const sets = Array.from(this.valuesByKey.values()); 647 return sets.some(set => set.has(value)); 648 } 649 getValuesForKey(key) { 650 const values = this.valuesByKey.get(key); 651 return values ? Array.from(values) : []; 652 } 653 getKeysForValue(value) { 654 return Array.from(this.valuesByKey) 655 .filter(([key, values]) => values.has(value)) 656 .map(([key, values]) => key); 657 } 658} 659 660class IndexedMultimap extends Multimap { 661 constructor() { 662 super(); 663 this.keysByValue = new Map; 664 } 665 get values() { 666 return Array.from(this.keysByValue.keys()); 667 } 668 add(key, value) { 669 super.add(key, value); 670 add(this.keysByValue, value, key); 671 } 672 delete(key, value) { 673 super.delete(key, value); 674 del(this.keysByValue, value, key); 675 } 676 hasValue(value) { 677 return this.keysByValue.has(value); 678 } 679 getKeysForValue(value) { 680 const set = this.keysByValue.get(value); 681 return set ? Array.from(set) : []; 682 } 683} 684 685class TokenListObserver { 686 constructor(element, attributeName, delegate) { 687 this.attributeObserver = new AttributeObserver(element, attributeName, this); 688 this.delegate = delegate; 689 this.tokensByElement = new Multimap; 690 } 691 get started() { 692 return this.attributeObserver.started; 693 } 694 start() { 695 this.attributeObserver.start(); 696 } 697 pause(callback) { 698 this.attributeObserver.pause(callback); 699 } 700 stop() { 701 this.attributeObserver.stop(); 702 } 703 refresh() { 704 this.attributeObserver.refresh(); 705 } 706 get element() { 707 return this.attributeObserver.element; 708 } 709 get attributeName() { 710 return this.attributeObserver.attributeName; 711 } 712 elementMatchedAttribute(element) { 713 this.tokensMatched(this.readTokensForElement(element)); 714 } 715 elementAttributeValueChanged(element) { 716 const [unmatchedTokens, matchedTokens] = this.refreshTokensForElement(element); 717 this.tokensUnmatched(unmatchedTokens); 718 this.tokensMatched(matchedTokens); 719 } 720 elementUnmatchedAttribute(element) { 721 this.tokensUnmatched(this.tokensByElement.getValuesForKey(element)); 722 } 723 tokensMatched(tokens) { 724 tokens.forEach(token => this.tokenMatched(token)); 725 } 726 tokensUnmatched(tokens) { 727 tokens.forEach(token => this.tokenUnmatched(token)); 728 } 729 tokenMatched(token) { 730 this.delegate.tokenMatched(token); 731 this.tokensByElement.add(token.element, token); 732 } 733 tokenUnmatched(token) { 734 this.delegate.tokenUnmatched(token); 735 this.tokensByElement.delete(token.element, token); 736 } 737 refreshTokensForElement(element) { 738 const previousTokens = this.tokensByElement.getValuesForKey(element); 739 const currentTokens = this.readTokensForElement(element); 740 const firstDifferingIndex = zip(previousTokens, currentTokens) 741 .findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken)); 742 if (firstDifferingIndex == -1) { 743 return [[], []]; 744 } 745 else { 746 return [previousTokens.slice(firstDifferingIndex), currentTokens.slice(firstDifferingIndex)]; 747 } 748 } 749 readTokensForElement(element) { 750 const attributeName = this.attributeName; 751 const tokenString = element.getAttribute(attributeName) || ""; 752 return parseTokenString(tokenString, element, attributeName); 753 } 754} 755function parseTokenString(tokenString, element, attributeName) { 756 return tokenString.trim().split(/\s+/).filter(content => content.length) 757 .map((content, index) => ({ element, attributeName, content, index })); 758} 759function zip(left, right) { 760 const length = Math.max(left.length, right.length); 761 return Array.from({ length }, (_, index) => [left[index], right[index]]); 762} 763function tokensAreEqual(left, right) { 764 return left && right && left.index == right.index && left.content == right.content; 765} 766 767class ValueListObserver { 768 constructor(element, attributeName, delegate) { 769 this.tokenListObserver = new TokenListObserver(element, attributeName, this); 770 this.delegate = delegate; 771 this.parseResultsByToken = new WeakMap; 772 this.valuesByTokenByElement = new WeakMap; 773 } 774 get started() { 775 return this.tokenListObserver.started; 776 } 777 start() { 778 this.tokenListObserver.start(); 779 } 780 stop() { 781 this.tokenListObserver.stop(); 782 } 783 refresh() { 784 this.tokenListObserver.refresh(); 785 } 786 get element() { 787 return this.tokenListObserver.element; 788 } 789 get attributeName() { 790 return this.tokenListObserver.attributeName; 791 } 792 tokenMatched(token) { 793 const { element } = token; 794 const { value } = this.fetchParseResultForToken(token); 795 if (value) { 796 this.fetchValuesByTokenForElement(element).set(token, value); 797 this.delegate.elementMatchedValue(element, value); 798 } 799 } 800 tokenUnmatched(token) { 801 const { element } = token; 802 const { value } = this.fetchParseResultForToken(token); 803 if (value) { 804 this.fetchValuesByTokenForElement(element).delete(token); 805 this.delegate.elementUnmatchedValue(element, value); 806 } 807 } 808 fetchParseResultForToken(token) { 809 let parseResult = this.parseResultsByToken.get(token); 810 if (!parseResult) { 811 parseResult = this.parseToken(token); 812 this.parseResultsByToken.set(token, parseResult); 813 } 814 return parseResult; 815 } 816 fetchValuesByTokenForElement(element) { 817 let valuesByToken = this.valuesByTokenByElement.get(element); 818 if (!valuesByToken) { 819 valuesByToken = new Map; 820 this.valuesByTokenByElement.set(element, valuesByToken); 821 } 822 return valuesByToken; 823 } 824 parseToken(token) { 825 try { 826 const value = this.delegate.parseValueForToken(token); 827 return { value }; 828 } 829 catch (error) { 830 return { error }; 831 } 832 } 833} 834 835class BindingObserver { 836 constructor(context, delegate) { 837 this.context = context; 838 this.delegate = delegate; 839 this.bindingsByAction = new Map; 840 } 841 start() { 842 if (!this.valueListObserver) { 843 this.valueListObserver = new ValueListObserver(this.element, this.actionAttribute, this); 844 this.valueListObserver.start(); 845 } 846 } 847 stop() { 848 if (this.valueListObserver) { 849 this.valueListObserver.stop(); 850 delete this.valueListObserver; 851 this.disconnectAllActions(); 852 } 853 } 854 get element() { 855 return this.context.element; 856 } 857 get identifier() { 858 return this.context.identifier; 859 } 860 get actionAttribute() { 861 return this.schema.actionAttribute; 862 } 863 get schema() { 864 return this.context.schema; 865 } 866 get bindings() { 867 return Array.from(this.bindingsByAction.values()); 868 } 869 connectAction(action) { 870 const binding = new Binding(this.context, action); 871 this.bindingsByAction.set(action, binding); 872 this.delegate.bindingConnected(binding); 873 } 874 disconnectAction(action) { 875 const binding = this.bindingsByAction.get(action); 876 if (binding) { 877 this.bindingsByAction.delete(action); 878 this.delegate.bindingDisconnected(binding); 879 } 880 } 881 disconnectAllActions() { 882 this.bindings.forEach(binding => this.delegate.bindingDisconnected(binding)); 883 this.bindingsByAction.clear(); 884 } 885 parseValueForToken(token) { 886 const action = Action.forToken(token); 887 if (action.identifier == this.identifier) { 888 return action; 889 } 890 } 891 elementMatchedValue(element, action) { 892 this.connectAction(action); 893 } 894 elementUnmatchedValue(element, action) { 895 this.disconnectAction(action); 896 } 897} 898 899class ValueObserver { 900 constructor(context, receiver) { 901 this.context = context; 902 this.receiver = receiver; 903 this.stringMapObserver = new StringMapObserver(this.element, this); 904 this.valueDescriptorMap = this.controller.valueDescriptorMap; 905 this.invokeChangedCallbacksForDefaultValues(); 906 } 907 start() { 908 this.stringMapObserver.start(); 909 } 910 stop() { 911 this.stringMapObserver.stop(); 912 } 913 get element() { 914 return this.context.element; 915 } 916 get controller() { 917 return this.context.controller; 918 } 919 getStringMapKeyForAttribute(attributeName) { 920 if (attributeName in this.valueDescriptorMap) { 921 return this.valueDescriptorMap[attributeName].name; 922 } 923 } 924 stringMapKeyAdded(key, attributeName) { 925 const descriptor = this.valueDescriptorMap[attributeName]; 926 if (!this.hasValue(key)) { 927 this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), descriptor.writer(descriptor.defaultValue)); 928 } 929 } 930 stringMapValueChanged(value, name, oldValue) { 931 const descriptor = this.valueDescriptorNameMap[name]; 932 if (value === null) 933 return; 934 if (oldValue === null) { 935 oldValue = descriptor.writer(descriptor.defaultValue); 936 } 937 this.invokeChangedCallback(name, value, oldValue); 938 } 939 stringMapKeyRemoved(key, attributeName, oldValue) { 940 const descriptor = this.valueDescriptorNameMap[key]; 941 if (this.hasValue(key)) { 942 this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), oldValue); 943 } 944 else { 945 this.invokeChangedCallback(key, descriptor.writer(descriptor.defaultValue), oldValue); 946 } 947 } 948 invokeChangedCallbacksForDefaultValues() { 949 for (const { key, name, defaultValue, writer } of this.valueDescriptors) { 950 if (defaultValue != undefined && !this.controller.data.has(key)) { 951 this.invokeChangedCallback(name, writer(defaultValue), undefined); 952 } 953 } 954 } 955 invokeChangedCallback(name, rawValue, rawOldValue) { 956 const changedMethodName = `${name}Changed`; 957 const changedMethod = this.receiver[changedMethodName]; 958 if (typeof changedMethod == "function") { 959 const descriptor = this.valueDescriptorNameMap[name]; 960 const value = descriptor.reader(rawValue); 961 let oldValue = rawOldValue; 962 if (rawOldValue) { 963 oldValue = descriptor.reader(rawOldValue); 964 } 965 changedMethod.call(this.receiver, value, oldValue); 966 } 967 } 968 get valueDescriptors() { 969 const { valueDescriptorMap } = this; 970 return Object.keys(valueDescriptorMap).map(key => valueDescriptorMap[key]); 971 } 972 get valueDescriptorNameMap() { 973 const descriptors = {}; 974 Object.keys(this.valueDescriptorMap).forEach(key => { 975 const descriptor = this.valueDescriptorMap[key]; 976 descriptors[descriptor.name] = descriptor; 977 }); 978 return descriptors; 979 } 980 hasValue(attributeName) { 981 const descriptor = this.valueDescriptorNameMap[attributeName]; 982 const hasMethodName = `has${capitalize(descriptor.name)}`; 983 return this.receiver[hasMethodName]; 984 } 985} 986 987class TargetObserver { 988 constructor(context, delegate) { 989 this.context = context; 990 this.delegate = delegate; 991 this.targetsByName = new Multimap; 992 } 993 start() { 994 if (!this.tokenListObserver) { 995 this.tokenListObserver = new TokenListObserver(this.element, this.attributeName, this); 996 this.tokenListObserver.start(); 997 } 998 } 999 stop() { 1000 if (this.tokenListObserver) { 1001 this.disconnectAllTargets(); 1002 this.tokenListObserver.stop(); 1003 delete this.tokenListObserver; 1004 } 1005 } 1006 tokenMatched({ element, content: name }) { 1007 if (this.scope.containsElement(element)) { 1008 this.connectTarget(element, name); 1009 } 1010 } 1011 tokenUnmatched({ element, content: name }) { 1012 this.disconnectTarget(element, name); 1013 } 1014 connectTarget(element, name) { 1015 var _a; 1016 if (!this.targetsByName.has(name, element)) { 1017 this.targetsByName.add(name, element); 1018 (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetConnected(element, name)); 1019 } 1020 } 1021 disconnectTarget(element, name) { 1022 var _a; 1023 if (this.targetsByName.has(name, element)) { 1024 this.targetsByName.delete(name, element); 1025 (_a = this.tokenListObserver) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.targetDisconnected(element, name)); 1026 } 1027 } 1028 disconnectAllTargets() { 1029 for (const name of this.targetsByName.keys) { 1030 for (const element of this.targetsByName.getValuesForKey(name)) { 1031 this.disconnectTarget(element, name); 1032 } 1033 } 1034 } 1035 get attributeName() { 1036 return `data-${this.context.identifier}-target`; 1037 } 1038 get element() { 1039 return this.context.element; 1040 } 1041 get scope() { 1042 return this.context.scope; 1043 } 1044} 1045 1046class Context { 1047 constructor(module, scope) { 1048 this.logDebugActivity = (functionName, detail = {}) => { 1049 const { identifier, controller, element } = this; 1050 detail = Object.assign({ identifier, controller, element }, detail); 1051 this.application.logDebugActivity(this.identifier, functionName, detail); 1052 }; 1053 this.module = module; 1054 this.scope = scope; 1055 this.controller = new module.controllerConstructor(this); 1056 this.bindingObserver = new BindingObserver(this, this.dispatcher); 1057 this.valueObserver = new ValueObserver(this, this.controller); 1058 this.targetObserver = new TargetObserver(this, this); 1059 try { 1060 this.controller.initialize(); 1061 this.logDebugActivity("initialize"); 1062 } 1063 catch (error) { 1064 this.handleError(error, "initializing controller"); 1065 } 1066 } 1067 connect() { 1068 this.bindingObserver.start(); 1069 this.valueObserver.start(); 1070 this.targetObserver.start(); 1071 try { 1072 this.controller.connect(); 1073 this.logDebugActivity("connect"); 1074 } 1075 catch (error) { 1076 this.handleError(error, "connecting controller"); 1077 } 1078 } 1079 disconnect() { 1080 try { 1081 this.controller.disconnect(); 1082 this.logDebugActivity("disconnect"); 1083 } 1084 catch (error) { 1085 this.handleError(error, "disconnecting controller"); 1086 } 1087 this.targetObserver.stop(); 1088 this.valueObserver.stop(); 1089 this.bindingObserver.stop(); 1090 } 1091 get application() { 1092 return this.module.application; 1093 } 1094 get identifier() { 1095 return this.module.identifier; 1096 } 1097 get schema() { 1098 return this.application.schema; 1099 } 1100 get dispatcher() { 1101 return this.application.dispatcher; 1102 } 1103 get element() { 1104 return this.scope.element; 1105 } 1106 get parentElement() { 1107 return this.element.parentElement; 1108 } 1109 handleError(error, message, detail = {}) { 1110 const { identifier, controller, element } = this; 1111 detail = Object.assign({ identifier, controller, element }, detail); 1112 this.application.handleError(error, `Error ${message}`, detail); 1113 } 1114 targetConnected(element, name) { 1115 this.invokeControllerMethod(`${name}TargetConnected`, element); 1116 } 1117 targetDisconnected(element, name) { 1118 this.invokeControllerMethod(`${name}TargetDisconnected`, element); 1119 } 1120 invokeControllerMethod(methodName, ...args) { 1121 const controller = this.controller; 1122 if (typeof controller[methodName] == "function") { 1123 controller[methodName](...args); 1124 } 1125 } 1126} 1127 1128function readInheritableStaticArrayValues(constructor, propertyName) { 1129 const ancestors = getAncestorsForConstructor(constructor); 1130 return Array.from(ancestors.reduce((values, constructor) => { 1131 getOwnStaticArrayValues(constructor, propertyName).forEach(name => values.add(name)); 1132 return values; 1133 }, new Set)); 1134} 1135function readInheritableStaticObjectPairs(constructor, propertyName) { 1136 const ancestors = getAncestorsForConstructor(constructor); 1137 return ancestors.reduce((pairs, constructor) => { 1138 pairs.push(...getOwnStaticObjectPairs(constructor, propertyName)); 1139 return pairs; 1140 }, []); 1141} 1142function getAncestorsForConstructor(constructor) { 1143 const ancestors = []; 1144 while (constructor) { 1145 ancestors.push(constructor); 1146 constructor = Object.getPrototypeOf(constructor); 1147 } 1148 return ancestors.reverse(); 1149} 1150function getOwnStaticArrayValues(constructor, propertyName) { 1151 const definition = constructor[propertyName]; 1152 return Array.isArray(definition) ? definition : []; 1153} 1154function getOwnStaticObjectPairs(constructor, propertyName) { 1155 const definition = constructor[propertyName]; 1156 return definition ? Object.keys(definition).map(key => [key, definition[key]]) : []; 1157} 1158 1159function bless(constructor) { 1160 return shadow(constructor, getBlessedProperties(constructor)); 1161} 1162function shadow(constructor, properties) { 1163 const shadowConstructor = extend(constructor); 1164 const shadowProperties = getShadowProperties(constructor.prototype, properties); 1165 Object.defineProperties(shadowConstructor.prototype, shadowProperties); 1166 return shadowConstructor; 1167} 1168function getBlessedProperties(constructor) { 1169 const blessings = readInheritableStaticArrayValues(constructor, "blessings"); 1170 return blessings.reduce((blessedProperties, blessing) => { 1171 const properties = blessing(constructor); 1172 for (const key in properties) { 1173 const descriptor = blessedProperties[key] || {}; 1174 blessedProperties[key] = Object.assign(descriptor, properties[key]); 1175 } 1176 return blessedProperties; 1177 }, {}); 1178} 1179function getShadowProperties(prototype, properties) { 1180 return getOwnKeys(properties).reduce((shadowProperties, key) => { 1181 const descriptor = getShadowedDescriptor(prototype, properties, key); 1182 if (descriptor) { 1183 Object.assign(shadowProperties, { [key]: descriptor }); 1184 } 1185 return shadowProperties; 1186 }, {}); 1187} 1188function getShadowedDescriptor(prototype, properties, key) { 1189 const shadowingDescriptor = Object.getOwnPropertyDescriptor(prototype, key); 1190 const shadowedByValue = shadowingDescriptor && "value" in shadowingDescriptor; 1191 if (!shadowedByValue) { 1192 const descriptor = Object.getOwnPropertyDescriptor(properties, key).value; 1193 if (shadowingDescriptor) { 1194 descriptor.get = shadowingDescriptor.get || descriptor.get; 1195 descriptor.set = shadowingDescriptor.set || descriptor.set; 1196 } 1197 return descriptor; 1198 } 1199} 1200const getOwnKeys = (() => { 1201 if (typeof Object.getOwnPropertySymbols == "function") { 1202 return (object) => [ 1203 ...Object.getOwnPropertyNames(object), 1204 ...Object.getOwnPropertySymbols(object) 1205 ]; 1206 } 1207 else { 1208 return Object.getOwnPropertyNames; 1209 } 1210})(); 1211const extend = (() => { 1212 function extendWithReflect(constructor) { 1213 function extended() { 1214 return Reflect.construct(constructor, arguments, new.target); 1215 } 1216 extended.prototype = Object.create(constructor.prototype, { 1217 constructor: { value: extended } 1218 }); 1219 Reflect.setPrototypeOf(extended, constructor); 1220 return extended; 1221 } 1222 function testReflectExtension() { 1223 const a = function () { this.a.call(this); }; 1224 const b = extendWithReflect(a); 1225 b.prototype.a = function () { }; 1226 return new b; 1227 } 1228 try { 1229 testReflectExtension(); 1230 return extendWithReflect; 1231 } 1232 catch (error) { 1233 return (constructor) => class extended extends constructor { 1234 }; 1235 } 1236})(); 1237 1238function blessDefinition(definition) { 1239 return { 1240 identifier: definition.identifier, 1241 controllerConstructor: bless(definition.controllerConstructor) 1242 }; 1243} 1244 1245class Module { 1246 constructor(application, definition) { 1247 this.application = application; 1248 this.definition = blessDefinition(definition); 1249 this.contextsByScope = new WeakMap; 1250 this.connectedContexts = new Set; 1251 } 1252 get identifier() { 1253 return this.definition.identifier; 1254 } 1255 get controllerConstructor() { 1256 return this.definition.controllerConstructor; 1257 } 1258 get contexts() { 1259 return Array.from(this.connectedContexts); 1260 } 1261 connectContextForScope(scope) { 1262 const context = this.fetchContextForScope(scope); 1263 this.connectedContexts.add(context); 1264 context.connect(); 1265 } 1266 disconnectContextForScope(scope) { 1267 const context = this.contextsByScope.get(scope); 1268 if (context) { 1269 this.connectedContexts.delete(context); 1270 context.disconnect(); 1271 } 1272 } 1273 fetchContextForScope(scope) { 1274 let context = this.contextsByScope.get(scope); 1275 if (!context) { 1276 context = new Context(this, scope); 1277 this.contextsByScope.set(scope, context); 1278 } 1279 return context; 1280 } 1281} 1282 1283class ClassMap { 1284 constructor(scope) { 1285 this.scope = scope; 1286 } 1287 has(name) { 1288 return this.data.has(this.getDataKey(name)); 1289 } 1290 get(name) { 1291 return this.getAll(name)[0]; 1292 } 1293 getAll(name) { 1294 const tokenString = this.data.get(this.getDataKey(name)) || ""; 1295 return tokenize(tokenString); 1296 } 1297 getAttributeName(name) { 1298 return this.data.getAttributeNameForKey(this.getDataKey(name)); 1299 } 1300 getDataKey(name) { 1301 return `${name}-class`; 1302 } 1303 get data() { 1304 return this.scope.data; 1305 } 1306} 1307 1308class DataMap { 1309 constructor(scope) { 1310 this.scope = scope; 1311 } 1312 get element() { 1313 return this.scope.element; 1314 } 1315 get identifier() { 1316 return this.scope.identifier; 1317 } 1318 get(key) { 1319 const name = this.getAttributeNameForKey(key); 1320 return this.element.getAttribute(name); 1321 } 1322 set(key, value) { 1323 const name = this.getAttributeNameForKey(key); 1324 this.element.setAttribute(name, value); 1325 return this.get(key); 1326 } 1327 has(key) { 1328 const name = this.getAttributeNameForKey(key); 1329 return this.element.hasAttribute(name); 1330 } 1331 delete(key) { 1332 if (this.has(key)) { 1333 const name = this.getAttributeNameForKey(key); 1334 this.element.removeAttribute(name); 1335 return true; 1336 } 1337 else { 1338 return false; 1339 } 1340 } 1341 getAttributeNameForKey(key) { 1342 return `data-${this.identifier}-${dasherize(key)}`; 1343 } 1344} 1345 1346class Guide { 1347 constructor(logger) { 1348 this.warnedKeysByObject = new WeakMap; 1349 this.logger = logger; 1350 } 1351 warn(object, key, message) { 1352 let warnedKeys = this.warnedKeysByObject.get(object); 1353 if (!warnedKeys) { 1354 warnedKeys = new Set; 1355 this.warnedKeysByObject.set(object, warnedKeys); 1356 } 1357 if (!warnedKeys.has(key)) { 1358 warnedKeys.add(key); 1359 this.logger.warn(message, object); 1360 } 1361 } 1362} 1363 1364function attributeValueContainsToken(attributeName, token) { 1365 return `[${attributeName}~="${token}"]`; 1366} 1367 1368class TargetSet { 1369 constructor(scope) { 1370 this.scope = scope; 1371 } 1372 get element() { 1373 return this.scope.element; 1374 } 1375 get identifier() { 1376 return this.scope.identifier; 1377 } 1378 get schema() { 1379 return this.scope.schema; 1380 } 1381 has(targetName) { 1382 return this.find(targetName) != null; 1383 } 1384 find(...targetNames) { 1385 return targetNames.reduce((target, targetName) => target 1386 || this.findTarget(targetName) 1387 || this.findLegacyTarget(targetName), undefined); 1388 } 1389 findAll(...targetNames) { 1390 return targetNames.reduce((targets, targetName) => [ 1391 ...targets, 1392 ...this.findAllTargets(targetName), 1393 ...this.findAllLegacyTargets(targetName) 1394 ], []); 1395 } 1396 findTarget(targetName) { 1397 const selector = this.getSelectorForTargetName(targetName); 1398 return this.scope.findElement(selector); 1399 } 1400 findAllTargets(targetName) { 1401 const selector = this.getSelectorForTargetName(targetName); 1402 return this.scope.findAllElements(selector); 1403 } 1404 getSelectorForTargetName(targetName) { 1405 const attributeName = this.schema.targetAttributeForScope(this.identifier); 1406 return attributeValueContainsToken(attributeName, targetName); 1407 } 1408 findLegacyTarget(targetName) { 1409 const selector = this.getLegacySelectorForTargetName(targetName); 1410 return this.deprecate(this.scope.findElement(selector), targetName); 1411 } 1412 findAllLegacyTargets(targetName) { 1413 const selector = this.getLegacySelectorForTargetName(targetName); 1414 return this.scope.findAllElements(selector).map(element => this.deprecate(element, targetName)); 1415 } 1416 getLegacySelectorForTargetName(targetName) { 1417 const targetDescriptor = `${this.identifier}.${targetName}`; 1418 return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor); 1419 } 1420 deprecate(element, targetName) { 1421 if (element) { 1422 const { identifier } = this; 1423 const attributeName = this.schema.targetAttribute; 1424 const revisedAttributeName = this.schema.targetAttributeForScope(identifier); 1425 this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". ` + 1426 `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`); 1427 } 1428 return element; 1429 } 1430 get guide() { 1431 return this.scope.guide; 1432 } 1433} 1434 1435class Scope { 1436 constructor(schema, element, identifier, logger) { 1437 this.targets = new TargetSet(this); 1438 this.classes = new ClassMap(this); 1439 this.data = new DataMap(this); 1440 this.containsElement = (element) => { 1441 return element.closest(this.controllerSelector) === this.element; 1442 }; 1443 this.schema = schema; 1444 this.element = element; 1445 this.identifier = identifier; 1446 this.guide = new Guide(logger); 1447 } 1448 findElement(selector) { 1449 return this.element.matches(selector) 1450 ? this.element 1451 : this.queryElements(selector).find(this.containsElement); 1452 } 1453 findAllElements(selector) { 1454 return [ 1455 ...this.element.matches(selector) ? [this.element] : [], 1456 ...this.queryElements(selector).filter(this.containsElement) 1457 ]; 1458 } 1459 queryElements(selector) { 1460 return Array.from(this.element.querySelectorAll(selector)); 1461 } 1462 get controllerSelector() { 1463 return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier); 1464 } 1465} 1466 1467class ScopeObserver { 1468 constructor(element, schema, delegate) { 1469 this.element = element; 1470 this.schema = schema; 1471 this.delegate = delegate; 1472 this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this); 1473 this.scopesByIdentifierByElement = new WeakMap; 1474 this.scopeReferenceCounts = new WeakMap; 1475 } 1476 start() { 1477 this.valueListObserver.start(); 1478 } 1479 stop() { 1480 this.valueListObserver.stop(); 1481 } 1482 get controllerAttribute() { 1483 return this.schema.controllerAttribute; 1484 } 1485 parseValueForToken(token) { 1486 const { element, content: identifier } = token; 1487 const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element); 1488 let scope = scopesByIdentifier.get(identifier); 1489 if (!scope) { 1490 scope = this.delegate.createScopeForElementAndIdentifier(element, identifier); 1491 scopesByIdentifier.set(identifier, scope); 1492 } 1493 return scope; 1494 } 1495 elementMatchedValue(element, value) { 1496 const referenceCount = (this.scopeReferenceCounts.get(value) || 0) + 1; 1497 this.scopeReferenceCounts.set(value, referenceCount); 1498 if (referenceCount == 1) { 1499 this.delegate.scopeConnected(value); 1500 } 1501 } 1502 elementUnmatchedValue(element, value) { 1503 const referenceCount = this.scopeReferenceCounts.get(value); 1504 if (referenceCount) { 1505 this.scopeReferenceCounts.set(value, referenceCount - 1); 1506 if (referenceCount == 1) { 1507 this.delegate.scopeDisconnected(value); 1508 } 1509 } 1510 } 1511 fetchScopesByIdentifierForElement(element) { 1512 let scopesByIdentifier = this.scopesByIdentifierByElement.get(element); 1513 if (!scopesByIdentifier) { 1514 scopesByIdentifier = new Map; 1515 this.scopesByIdentifierByElement.set(element, scopesByIdentifier); 1516 } 1517 return scopesByIdentifier; 1518 } 1519} 1520 1521class Router { 1522 constructor(application) { 1523 this.application = application; 1524 this.scopeObserver = new ScopeObserver(this.element, this.schema, this); 1525 this.scopesByIdentifier = new Multimap; 1526 this.modulesByIdentifier = new Map; 1527 } 1528 get element() { 1529 return this.application.element; 1530 } 1531 get schema() { 1532 return this.application.schema; 1533 } 1534 get logger() { 1535 return this.application.logger; 1536 } 1537 get controllerAttribute() { 1538 return this.schema.controllerAttribute; 1539 } 1540 get modules() { 1541 return Array.from(this.modulesByIdentifier.values()); 1542 } 1543 get contexts() { 1544 return this.modules.reduce((contexts, module) => contexts.concat(module.contexts), []); 1545 } 1546 start() { 1547 this.scopeObserver.start(); 1548 } 1549 stop() { 1550 this.scopeObserver.stop(); 1551 } 1552 loadDefinition(definition) { 1553 this.unloadIdentifier(definition.identifier); 1554 const module = new Module(this.application, definition); 1555 this.connectModule(module); 1556 } 1557 unloadIdentifier(identifier) { 1558 const module = this.modulesByIdentifier.get(identifier); 1559 if (module) { 1560 this.disconnectModule(module); 1561 } 1562 } 1563 getContextForElementAndIdentifier(element, identifier) { 1564 const module = this.modulesByIdentifier.get(identifier); 1565 if (module) { 1566 return module.contexts.find(context => context.element == element); 1567 } 1568 } 1569 handleError(error, message, detail) { 1570 this.application.handleError(error, message, detail); 1571 } 1572 createScopeForElementAndIdentifier(element, identifier) { 1573 return new Scope(this.schema, element, identifier, this.logger); 1574 } 1575 scopeConnected(scope) { 1576 this.scopesByIdentifier.add(scope.identifier, scope); 1577 const module = this.modulesByIdentifier.get(scope.identifier); 1578 if (module) { 1579 module.connectContextForScope(scope); 1580 } 1581 } 1582 scopeDisconnected(scope) { 1583 this.scopesByIdentifier.delete(scope.identifier, scope); 1584 const module = this.modulesByIdentifier.get(scope.identifier); 1585 if (module) { 1586 module.disconnectContextForScope(scope); 1587 } 1588 } 1589 connectModule(module) { 1590 this.modulesByIdentifier.set(module.identifier, module); 1591 const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier); 1592 scopes.forEach(scope => module.connectContextForScope(scope)); 1593 } 1594 disconnectModule(module) { 1595 this.modulesByIdentifier.delete(module.identifier); 1596 const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier); 1597 scopes.forEach(scope => module.disconnectContextForScope(scope)); 1598 } 1599} 1600 1601const defaultSchema = { 1602 controllerAttribute: "data-controller", 1603 actionAttribute: "data-action", 1604 targetAttribute: "data-target", 1605 targetAttributeForScope: identifier => `data-${identifier}-target` 1606}; 1607 1608class Application { 1609 constructor(element = document.documentElement, schema = defaultSchema) { 1610 this.logger = console; 1611 this.debug = false; 1612 this.logDebugActivity = (identifier, functionName, detail = {}) => { 1613 if (this.debug) { 1614 this.logFormattedMessage(identifier, functionName, detail); 1615 } 1616 }; 1617 this.element = element; 1618 this.schema = schema; 1619 this.dispatcher = new Dispatcher(this); 1620 this.router = new Router(this); 1621 } 1622 static start(element, schema) { 1623 const application = new Application(element, schema); 1624 application.start(); 1625 return application; 1626 } 1627 async start() { 1628 await domReady(); 1629 this.logDebugActivity("application", "starting"); 1630 this.dispatcher.start(); 1631 this.router.start(); 1632 this.logDebugActivity("application", "start"); 1633 } 1634 stop() { 1635 this.logDebugActivity("application", "stopping"); 1636 this.dispatcher.stop(); 1637 this.router.stop(); 1638 this.logDebugActivity("application", "stop"); 1639 } 1640 register(identifier, controllerConstructor) { 1641 if (controllerConstructor.shouldLoad) { 1642 this.load({ identifier, controllerConstructor }); 1643 } 1644 } 1645 load(head, ...rest) { 1646 const definitions = Array.isArray(head) ? head : [head, ...rest]; 1647 definitions.forEach(definition => this.router.loadDefinition(definition)); 1648 } 1649 unload(head, ...rest) { 1650 const identifiers = Array.isArray(head) ? head : [head, ...rest]; 1651 identifiers.forEach(identifier => this.router.unloadIdentifier(identifier)); 1652 } 1653 get controllers() { 1654 return this.router.contexts.map(context => context.controller); 1655 } 1656 getControllerForElementAndIdentifier(element, identifier) { 1657 const context = this.router.getContextForElementAndIdentifier(element, identifier); 1658 return context ? context.controller : null; 1659 } 1660 handleError(error, message, detail) { 1661 var _a; 1662 this.logger.error(`%s\n\n%o\n\n%o`, message, error, detail); 1663 (_a = window.onerror) === null || _a === void 0 ? void 0 : _a.call(window, message, "", 0, 0, error); 1664 } 1665 logFormattedMessage(identifier, functionName, detail = {}) { 1666 detail = Object.assign({ application: this }, detail); 1667 this.logger.groupCollapsed(`${identifier} #${functionName}`); 1668 this.logger.log("details:", Object.assign({}, detail)); 1669 this.logger.groupEnd(); 1670 } 1671} 1672function domReady() { 1673 return new Promise(resolve => { 1674 if (document.readyState == "loading") { 1675 document.addEventListener("DOMContentLoaded", () => resolve()); 1676 } 1677 else { 1678 resolve(); 1679 } 1680 }); 1681} 1682 1683function ClassPropertiesBlessing(constructor) { 1684 const classes = readInheritableStaticArrayValues(constructor, "classes"); 1685 return classes.reduce((properties, classDefinition) => { 1686 return Object.assign(properties, propertiesForClassDefinition(classDefinition)); 1687 }, {}); 1688} 1689function propertiesForClassDefinition(key) { 1690 return { 1691 [`${key}Class`]: { 1692 get() { 1693 const { classes } = this; 1694 if (classes.has(key)) { 1695 return classes.get(key); 1696 } 1697 else { 1698 const attribute = classes.getAttributeName(key); 1699 throw new Error(`Missing attribute "${attribute}"`); 1700 } 1701 } 1702 }, 1703 [`${key}Classes`]: { 1704 get() { 1705 return this.classes.getAll(key); 1706 } 1707 }, 1708 [`has${capitalize(key)}Class`]: { 1709 get() { 1710 return this.classes.has(key); 1711 } 1712 } 1713 }; 1714} 1715 1716function TargetPropertiesBlessing(constructor) { 1717 const targets = readInheritableStaticArrayValues(constructor, "targets"); 1718 return targets.reduce((properties, targetDefinition) => { 1719 return Object.assign(properties, propertiesForTargetDefinition(targetDefinition)); 1720 }, {}); 1721} 1722function propertiesForTargetDefinition(name) { 1723 return { 1724 [`${name}Target`]: { 1725 get() { 1726 const target = this.targets.find(name); 1727 if (target) { 1728 return target; 1729 } 1730 else { 1731 throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`); 1732 } 1733 } 1734 }, 1735 [`${name}Targets`]: { 1736 get() { 1737 return this.targets.findAll(name); 1738 } 1739 }, 1740 [`has${capitalize(name)}Target`]: { 1741 get() { 1742 return this.targets.has(name); 1743 } 1744 } 1745 }; 1746} 1747 1748function ValuePropertiesBlessing(constructor) { 1749 const valueDefinitionPairs = readInheritableStaticObjectPairs(constructor, "values"); 1750 const propertyDescriptorMap = { 1751 valueDescriptorMap: { 1752 get() { 1753 return valueDefinitionPairs.reduce((result, valueDefinitionPair) => { 1754 const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair); 1755 const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key); 1756 return Object.assign(result, { [attributeName]: valueDescriptor }); 1757 }, {}); 1758 } 1759 } 1760 }; 1761 return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => { 1762 return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair)); 1763 }, propertyDescriptorMap); 1764} 1765function propertiesForValueDefinitionPair(valueDefinitionPair) { 1766 const definition = parseValueDefinitionPair(valueDefinitionPair);
1767 const { key, name, reader: read, writer: write } = definition; 1768 return { 1769 [name]: { 1770 get() { 1771 const value = this.data.get(key); 1772 if (value !== null) { 1773 return read(value); 1774 } 1775 else { 1776 return definition.defaultValue; 1777 } 1778 }, 1779 set(value) { 1780 if (value === undefined) { 1781 this.data.delete(key); 1782 } 1783 else { 1784 this.data.set(key, write(value)); 1785 } 1786 } 1787 }, 1788 [`has${capitalize(name)}`]: { 1789 get() { 1790 return this.data.has(key) || definition.hasCustomDefaultValue; 1791 } 1792 } 1793 }; 1794} 1795function parseValueDefinitionPair([token, typeDefinition]) { 1796 return valueDescriptorForTokenAndTypeDefinition(token, typeDefinition); 1797} 1798function parseValueTypeConstant(constant) { 1799 switch (constant) { 1800 case Array: return "array"; 1801 case Boolean: return "boolean"; 1802 case Number: return "number"; 1803 case Object: return "object"; 1804 case String: return "string"; 1805 } 1806} 1807function parseValueTypeDefault(defaultValue) { 1808 switch (typeof defaultValue) { 1809 case "boolean": return "boolean"; 1810 case "number": return "number"; 1811 case "string": return "string"; 1812 } 1813 if (Array.isArray(defaultValue)) 1814 return "array"; 1815 if (Object.prototype.toString.call(defaultValue) === "[object Object]") 1816 return "object"; 1817} 1818function parseValueTypeObject(typeObject) { 1819 const typeFromObject = parseValueTypeConstant(typeObject.type); 1820 if (typeFromObject) { 1821 const defaultValueType = parseValueTypeDefault(typeObject.default); 1822 if (typeFromObject !== defaultValueType) { 1823 throw new Error(`Type "${typeFromObject}" must match the type of the default value. Given default value: "${typeObject.default}" as "${defaultValueType}"`); 1824 } 1825 return typeFromObject; 1826 } 1827} 1828function parseValueTypeDefinition(typeDefinition) { 1829 const typeFromObject = parseValueTypeObject(typeDefinition); 1830 const typeFromDefaultValue = parseValueTypeDefault(typeDefinition); 1831 const typeFromConstant = parseValueTypeConstant(typeDefinition); 1832 const type = typeFromObject || typeFromDefaultValue || typeFromConstant; 1833 if (type) 1834 return type; 1835 throw new Error(`Unknown value type "${typeDefinition}"`); 1836} 1837function defaultValueForDefinition(typeDefinition) { 1838 const constant = parseValueTypeConstant(typeDefinition); 1839 if (constant) 1840 return defaultValuesByType[constant]; 1841 const defaultValue = typeDefinition.default; 1842 if (defaultValue !== undefined) 1843 return defaultValue; 1844 return typeDefinition; 1845} 1846function valueDescriptorForTokenAndTypeDefinition(token, typeDefinition) { 1847 const key = `${dasherize(token)}-value`; 1848 const type = parseValueTypeDefinition(typeDefinition); 1849 return { 1850 type, 1851 key, 1852 name: camelize(key), 1853 get defaultValue() { return defaultValueForDefinition(typeDefinition); }, 1854 get hasCustomDefaultValue() { return parseValueTypeDefault(typeDefinition) !== undefined; }, 1855 reader: readers[type], 1856 writer: writers[type] || writers.default 1857 }; 1858} 1859const defaultValuesByType = { 1860 get array() { return []; }, 1861 boolean: false, 1862 number: 0, 1863 get object() { return {}; }, 1864 string: "" 1865}; 1866const readers = { 1867 array(value) { 1868 const array = JSON.parse(value); 1869 if (!Array.isArray(array)) { 1870 throw new TypeError("Expected array"); 1871 } 1872 return array; 1873 }, 1874 boolean(value) { 1875 return !(value == "0" || value == "false"); 1876 }, 1877 number(value) { 1878 return Number(value); 1879 }, 1880 object(value) { 1881 const object = JSON.parse(value); 1882 if (object === null || typeof object != "object" || Array.isArray(object)) { 1883 throw new TypeError("Expected object"); 1884 } 1885 return object; 1886 }, 1887 string(value) { 1888 return value; 1889 } 1890}; 1891const writers = { 1892 default: writeString, 1893 array: writeJSON, 1894 object: writeJSON 1895}; 1896function writeJSON(value) { 1897 return JSON.stringify(value); 1898} 1899function writeString(value) { 1900 return `${value}`; 1901} 1902 1903class Controller { 1904 constructor(context) { 1905 this.context = context; 1906 } 1907 static get shouldLoad() { 1908 return true; 1909 } 1910 get application() { 1911 return this.context.application; 1912 } 1913 get scope() { 1914 return this.context.scope; 1915 }
1916 get element() { 1917 return this.scope.element; 1918 } 1919 get identifier() { 1920 return this.scope.identifier; 1921 } 1922 get targets() { 1923 return this.scope.targets; 1924 } 1925 get classes() { 1926 return this.scope.classes; 1927 } 1928 get data() { 1929 return this.scope.data; 1930 } 1931 initialize() { 1932 } 1933 connect() { 1934 } 1935 disconnect() { 1936 } 1937 dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}) { 1938 const type = prefix ? `${prefix}:${eventName}` : eventName; 1939 const event = new CustomEvent(type, { detail, bubbles, cancelable }); 1940 target.dispatchEvent(event); 1941 return event; 1942 } 1943} 1944Controller.blessings = [ClassPropertiesBlessing, TargetPropertiesBlessing, ValuePropertiesBlessing]; 1945Controller.targets = []; 1946Controller.values = {}; 1947 1948export { Application, AttributeObserver, Context, Controller, ElementObserver, IndexedMultimap, Multimap, 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.