Skip to content

Instantly share code, notes, and snippets.

@javan
Created April 24, 2018 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javan/999900e42023e105de55733147f36af3 to your computer and use it in GitHub Desktop.
Save javan/999900e42023e105de55733147f36af3 to your computer and use it in GitHub Desktop.
/*
Stimulus 1.0.1
Copyright © 2018 Basecamp, LLC
*/
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.Stimulus = {});
})(this, function(exports) {
"use strict";
var descriptorPattern = /^((.+?)(@(window|document))?->)?(.+?)(#(.+))?$/;
function parseDescriptorString(descriptorString) {
var source = descriptorString.trim();
var matches = source.match(descriptorPattern) || [];
return {
eventTarget: parseEventTarget(matches[4]),
eventName: matches[2],
identifier: matches[5],
methodName: matches[7]
};
}
function parseEventTarget(eventTargetName) {
if (eventTargetName == "window") {
return window;
} else if (eventTargetName == "document") {
return document;
}
}
function stringifyEventTarget(eventTarget) {
if (eventTarget == window) {
return "window";
} else if (eventTarget == document) {
return "document";
}
}
var Action = function() {
function Action(element, descriptor) {
this.element = element;
this.eventTarget = descriptor.eventTarget || element;
this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name");
this.identifier = descriptor.identifier || error("missing identifier");
this.methodName = descriptor.methodName || error("missing method name");
}
Action.forElementWithDescriptorString = function(element, descriptorString) {
return new this(element, parseDescriptorString(descriptorString));
};
Action.prototype.toString = function() {
var eventNameSuffix = this.eventTargetName ? "@" + this.eventTargetName : "";
return "" + this.eventName + eventNameSuffix + "->" + this.identifier + "#" + this.methodName;
};
Object.defineProperty(Action.prototype, "eventTargetName", {
get: function() {
return stringifyEventTarget(this.eventTarget);
},
enumerable: true,
configurable: true
});
return Action;
}();
var defaultEventNames = {
a: function(e) {
return "click";
},
button: function(e) {
return "click";
},
form: function(e) {
return "submit";
},
input: function(e) {
return e.getAttribute("type") == "submit" ? "click" : "change";
},
select: function(e) {
return "change";
},
textarea: function(e) {
return "change";
}
};
function getDefaultEventNameForElement(element) {
var tagName = element.tagName.toLowerCase();
if (tagName in defaultEventNames) {
return defaultEventNames[tagName](element);
}
}
function error(message) {
throw new Error(message);
}
var ElementObserver = function() {
function ElementObserver(element, delegate) {
var _this = this;
this.element = element;
this.started = false;
this.delegate = delegate;
this.elements = new Set();
this.mutationObserver = new MutationObserver(function(mutations) {
return _this.processMutations(mutations);
});
}
ElementObserver.prototype.start = function() {
if (!this.started) {
this.started = true;
this.mutationObserver.observe(this.element, {
attributes: true,
childList: true,
subtree: true
});
this.refresh();
}
};
ElementObserver.prototype.stop = function() {
if (this.started) {
this.mutationObserver.takeRecords();
this.mutationObserver.disconnect();
this.started = false;
}
};
ElementObserver.prototype.refresh = function() {
if (this.started) {
var matches = new Set(this.matchElementsInTree());
for (var _i = 0, _a = Array.from(this.elements); _i < _a.length; _i++) {
var element = _a[_i];
if (!matches.has(element)) {
this.removeElement(element);
}
}
for (var _b = 0, _c = Array.from(matches); _b < _c.length; _b++) {
var element = _c[_b];
this.addElement(element);
}
}
};
ElementObserver.prototype.processMutations = function(mutations) {
if (this.started) {
for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
var mutation = mutations_1[_i];
this.processMutation(mutation);
}
}
};
ElementObserver.prototype.processMutation = function(mutation) {
if (mutation.type == "attributes") {
this.processAttributeChange(mutation.target, mutation.attributeName);
} else if (mutation.type == "childList") {
this.processRemovedNodes(mutation.removedNodes);
this.processAddedNodes(mutation.addedNodes);
}
};
ElementObserver.prototype.processAttributeChange = function(node, attributeName) {
var element = node;
if (this.elements.has(element)) {
if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
this.delegate.elementAttributeChanged(element, attributeName);
} else {
this.removeElement(element);
}
} else if (this.matchElement(element)) {
this.addElement(element);
}
};
ElementObserver.prototype.processRemovedNodes = function(nodes) {
for (var _i = 0, _a = Array.from(nodes); _i < _a.length; _i++) {
var node = _a[_i];
this.processNode(node, this.removeElement);
}
};
ElementObserver.prototype.processAddedNodes = function(nodes) {
for (var _i = 0, _a = Array.from(nodes); _i < _a.length; _i++) {
var node = _a[_i];
this.processNode(node, this.addElement);
}
};
ElementObserver.prototype.matchElement = function(element) {
return this.delegate.matchElement(element);
};
ElementObserver.prototype.matchElementsInTree = function(tree) {
if (tree === void 0) {
tree = this.element;
}
return this.delegate.matchElementsInTree(tree);
};
ElementObserver.prototype.processNode = function(node, processor) {
var tree = this.elementFromNode(node);
if (tree) {
for (var _i = 0, _a = this.matchElementsInTree(tree); _i < _a.length; _i++) {
var element = _a[_i];
processor.call(this, element);
}
}
};
ElementObserver.prototype.elementFromNode = function(node) {
if (node.nodeType == Node.ELEMENT_NODE) {
return node;
}
};
ElementObserver.prototype.addElement = function(element) {
if (!this.elements.has(element)) {
this.elements.add(element);
if (this.delegate.elementMatched) {
this.delegate.elementMatched(element);
}
}
};
ElementObserver.prototype.removeElement = function(element) {
if (this.elements.has(element)) {
this.elements.delete(element);
if (this.delegate.elementUnmatched) {
this.delegate.elementUnmatched(element);
}
}
};
return ElementObserver;
}();
var AttributeObserver = function() {
function AttributeObserver(element, attributeName, delegate) {
this.attributeName = attributeName;
this.delegate = delegate;
this.elementObserver = new ElementObserver(element, this);
}
Object.defineProperty(AttributeObserver.prototype, "element", {
get: function() {
return this.elementObserver.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(AttributeObserver.prototype, "selector", {
get: function() {
return "[" + this.attributeName + "]";
},
enumerable: true,
configurable: true
});
AttributeObserver.prototype.start = function() {
this.elementObserver.start();
};
AttributeObserver.prototype.stop = function() {
this.elementObserver.stop();
};
AttributeObserver.prototype.matchElement = function(element) {
return element.hasAttribute(this.attributeName);
};
AttributeObserver.prototype.matchElementsInTree = function(tree) {
var match = this.matchElement(tree) ? [ tree ] : [];
var matches = Array.from(tree.querySelectorAll(this.selector));
return match.concat(matches);
};
AttributeObserver.prototype.elementMatched = function(element) {
if (this.delegate.elementMatchedAttribute) {
this.delegate.elementMatchedAttribute(element, this.attributeName);
}
};
AttributeObserver.prototype.elementUnmatched = function(element) {
if (this.delegate.elementUnmatchedAttribute) {
this.delegate.elementUnmatchedAttribute(element, this.attributeName);
}
};
AttributeObserver.prototype.elementAttributeChanged = function(element, attributeName) {
if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) {
this.delegate.elementAttributeValueChanged(element, attributeName);
}
};
return AttributeObserver;
}();
function add(map, key, value) {
fetch(map, key).add(value);
}
function del(map, key, value) {
fetch(map, key).delete(value);
prune(map, key);
}
function fetch(map, key) {
var values = map.get(key);
if (!values) {
values = new Set();
map.set(key, values);
}
return values;
}
function prune(map, key) {
var values = map.get(key);
if (values != null && values.size == 0) {
map.delete(key);
}
}
var Multimap = function() {
function Multimap() {
this.valuesByKey = new Map();
}
Object.defineProperty(Multimap.prototype, "values", {
get: function() {
var sets = Array.from(this.valuesByKey.values());
return sets.reduce(function(values, set) {
return values.concat(Array.from(set));
}, []);
},
enumerable: true,
configurable: true
});
Object.defineProperty(Multimap.prototype, "size", {
get: function() {
var sets = Array.from(this.valuesByKey.values());
return sets.reduce(function(size, set) {
return size + set.size;
}, 0);
},
enumerable: true,
configurable: true
});
Multimap.prototype.add = function(key, value) {
add(this.valuesByKey, key, value);
};
Multimap.prototype.delete = function(key, value) {
del(this.valuesByKey, key, value);
};
Multimap.prototype.has = function(key, value) {
var values = this.valuesByKey.get(key);
return values != null && values.has(value);
};
Multimap.prototype.hasKey = function(key) {
return this.valuesByKey.has(key);
};
Multimap.prototype.hasValue = function(value) {
var sets = Array.from(this.valuesByKey.values());
return sets.some(function(set) {
return set.has(value);
});
};
Multimap.prototype.getValuesForKey = function(key) {
var values = this.valuesByKey.get(key);
return values ? Array.from(values) : [];
};
Multimap.prototype.getKeysForValue = function(value) {
return Array.from(this.valuesByKey).filter(function(_a) {
var key = _a[0], values = _a[1];
return values.has(value);
}).map(function(_a) {
var key = _a[0], values = _a[1];
return key;
});
};
return Multimap;
}();
var __extends = undefined && undefined.__extends || function() {
var extendStatics = Object.setPrototypeOf || {
__proto__: []
} instanceof Array && function(d, b) {
d.__proto__ = b;
} || function(d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
};
return function(d, b) {
extendStatics(d, b);
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
}();
var IndexedMultimap = function(_super) {
__extends(IndexedMultimap, _super);
function IndexedMultimap() {
var _this = _super.call(this) || this;
_this.keysByValue = new Map();
return _this;
}
Object.defineProperty(IndexedMultimap.prototype, "values", {
get: function() {
return Array.from(this.keysByValue.keys());
},
enumerable: true,
configurable: true
});
IndexedMultimap.prototype.add = function(key, value) {
_super.prototype.add.call(this, key, value);
add(this.keysByValue, value, key);
};
IndexedMultimap.prototype.delete = function(key, value) {
_super.prototype.delete.call(this, key, value);
del(this.keysByValue, value, key);
};
IndexedMultimap.prototype.hasValue = function(value) {
return this.keysByValue.has(value);
};
IndexedMultimap.prototype.getKeysForValue = function(value) {
var set = this.keysByValue.get(value);
return set ? Array.from(set) : [];
};
return IndexedMultimap;
}(Multimap);
var TokenListObserver = function() {
function TokenListObserver(element, attributeName, delegate) {
this.attributeName = attributeName;
this.delegate = delegate;
this.elementObserver = new ElementObserver(element, this);
this.tokensByElement = new IndexedMultimap();
}
Object.defineProperty(TokenListObserver.prototype, "started", {
get: function() {
return this.elementObserver.started;
},
enumerable: true,
configurable: true
});
TokenListObserver.prototype.start = function() {
this.elementObserver.start();
};
TokenListObserver.prototype.stop = function() {
this.elementObserver.stop();
};
TokenListObserver.prototype.refresh = function() {
this.elementObserver.refresh();
};
Object.defineProperty(TokenListObserver.prototype, "element", {
get: function() {
return this.elementObserver.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TokenListObserver.prototype, "selector", {
get: function() {
return "[" + this.attributeName + "]";
},
enumerable: true,
configurable: true
});
TokenListObserver.prototype.getElementsMatchingToken = function(token) {
return this.tokensByElement.getKeysForValue(token);
};
TokenListObserver.prototype.matchElement = function(element) {
return element.hasAttribute(this.attributeName);
};
TokenListObserver.prototype.matchElementsInTree = function(tree) {
var match = this.matchElement(tree) ? [ tree ] : [];
var matches = Array.from(tree.querySelectorAll(this.selector));
return match.concat(matches);
};
TokenListObserver.prototype.elementMatched = function(element) {
var newTokens = Array.from(this.readTokenSetForElement(element));
for (var _i = 0, newTokens_1 = newTokens; _i < newTokens_1.length; _i++) {
var token = newTokens_1[_i];
this.elementMatchedToken(element, token);
}
};
TokenListObserver.prototype.elementUnmatched = function(element) {
var tokens = this.getTokensForElement(element);
for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
var token = tokens_1[_i];
this.elementUnmatchedToken(element, token);
}
};
TokenListObserver.prototype.elementAttributeChanged = function(element) {
var newTokenSet = this.readTokenSetForElement(element);
for (var _i = 0, _a = Array.from(newTokenSet); _i < _a.length; _i++) {
var token = _a[_i];
this.elementMatchedToken(element, token);
}
for (var _b = 0, _c = this.getTokensForElement(element); _b < _c.length; _b++) {
var token = _c[_b];
if (!newTokenSet.has(token)) {
this.elementUnmatchedToken(element, token);
}
}
};
TokenListObserver.prototype.elementMatchedToken = function(element, token) {
if (!this.tokensByElement.has(element, token)) {
this.tokensByElement.add(element, token);
if (this.delegate.elementMatchedTokenForAttribute) {
this.delegate.elementMatchedTokenForAttribute(element, token, this.attributeName);
}
}
};
TokenListObserver.prototype.elementUnmatchedToken = function(element, token) {
if (this.tokensByElement.has(element, token)) {
this.tokensByElement.delete(element, token);
if (this.delegate.elementUnmatchedTokenForAttribute) {
this.delegate.elementUnmatchedTokenForAttribute(element, token, this.attributeName);
}
}
};
TokenListObserver.prototype.getTokensForElement = function(element) {
return this.tokensByElement.getValuesForKey(element);
};
TokenListObserver.prototype.readTokenSetForElement = function(element) {
var tokens = new Set();
var value = element.getAttribute(this.attributeName) || "";
for (var _i = 0, _a = value.split(/\s+/); _i < _a.length; _i++) {
var token = _a[_i];
if (token.length) {
tokens.add(token);
}
}
return tokens;
};
return TokenListObserver;
}();
var TokenObserver = function() {
function TokenObserver(element, attributeName, delegate) {
this.tokenListObserver = new TokenListObserver(element, attributeName, this);
this.delegate = delegate;
this.parseResultsByElementAndValue = new WeakMap();
}
Object.defineProperty(TokenObserver.prototype, "started", {
get: function() {
return this.tokenListObserver.started;
},
enumerable: true,
configurable: true
});
TokenObserver.prototype.start = function() {
this.tokenListObserver.start();
};
TokenObserver.prototype.stop = function() {
this.tokenListObserver.stop();
};
TokenObserver.prototype.refresh = function() {
this.tokenListObserver.refresh();
};
Object.defineProperty(TokenObserver.prototype, "element", {
get: function() {
return this.tokenListObserver.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TokenObserver.prototype, "attributeName", {
get: function() {
return this.tokenListObserver.attributeName;
},
enumerable: true,
configurable: true
});
TokenObserver.prototype.elementMatchedTokenForAttribute = function(element, value, attributeName) {
var _a = this.fetchParseResultForElementAndValue(element, value), token = _a.token, error = _a.error;
if (token) {
this.delegate.elementMatchedToken(token);
} else if (error && this.delegate.handleErrorParsingTokenSource) {
this.delegate.handleErrorParsingTokenSource(error, {
element: element,
attributeName: attributeName,
value: value
});
}
};
TokenObserver.prototype.elementUnmatchedTokenForAttribute = function(element, value, attributeName) {
var token = this.fetchParseResultForElementAndValue(element, value).token;
if (token) {
this.delegate.elementUnmatchedToken(token);
}
};
TokenObserver.prototype.fetchParseResultForElementAndValue = function(element, value) {
var parseResultsByValue = this.fetchParseResultsByValueForElement(element);
var parseResult = parseResultsByValue.get(value);
if (!parseResult) {
parseResult = this.parseTokenSource({
element: element,
attributeName: this.attributeName,
value: value
});
parseResultsByValue.set(value, parseResult);
}
return parseResult;
};
TokenObserver.prototype.fetchParseResultsByValueForElement = function(element) {
var parseResultsByValue = this.parseResultsByElementAndValue.get(element);
if (!parseResultsByValue) {
parseResultsByValue = new Map();
this.parseResultsByElementAndValue.set(element, parseResultsByValue);
}
return parseResultsByValue;
};
TokenObserver.prototype.parseTokenSource = function(source) {
try {
return {
token: {
source: source,
value: this.delegate.parseValueFromTokenSource(source)
}
};
} catch (error) {
return {
error: error
};
}
};
return TokenObserver;
}();
var ActionObserver = function() {
function ActionObserver(element, schema, delegate) {
this.element = element;
this.schema = schema;
this.delegate = delegate;
this.tokenObserver = new TokenObserver(element, this.actionAttribute, this);
}
ActionObserver.prototype.start = function() {
this.tokenObserver.start();
};
ActionObserver.prototype.stop = function() {
this.tokenObserver.stop();
};
Object.defineProperty(ActionObserver.prototype, "actionAttribute", {
get: function() {
return this.schema.actionAttribute;
},
enumerable: true,
configurable: true
});
ActionObserver.prototype.parseValueFromTokenSource = function(source) {
return Action.forElementWithDescriptorString(source.element, source.value);
};
ActionObserver.prototype.handleErrorParsingTokenSource = function(error, source) {
var location = "<" + source.element.tagName.toLowerCase() + " " + source.attributeName + ">";
this.delegate.handleError(error, 'parsing action descriptor "' + source.value + '" in ' + location, source);
};
ActionObserver.prototype.elementMatchedToken = function(token) {
this.delegate.actionConnected(token.value);
};
ActionObserver.prototype.elementUnmatchedToken = function(token) {
this.delegate.actionDisconnected(token.value);
};
return ActionObserver;
}();
var EventListener = function() {
function EventListener(context, action) {
this.context = context;
this.action = action;
}
Object.defineProperty(EventListener.prototype, "eventTarget", {
get: function() {
return this.action.eventTarget;
},
enumerable: true,
configurable: true
});
EventListener.prototype.connect = function() {
this.eventTarget.addEventListener(this.eventName, this, false);
};
EventListener.prototype.disconnect = function() {
this.eventTarget.removeEventListener(this.eventName, this, false);
};
EventListener.prototype.handleEvent = function(event) {
if (this.willBeInvokedByEvent(event)) {
this.invokeWithEvent(event);
}
};
Object.defineProperty(EventListener.prototype, "eventName", {
get: function() {
return this.action.eventName;
},
enumerable: true,
configurable: true
});
Object.defineProperty(EventListener.prototype, "method", {
get: function() {
var method = this.controller[this.methodName];
if (typeof method == "function") {
return method;
}
throw new Error('Action "' + this.action + '" references undefined method "' + this.methodName + '"');
},
enumerable: true,
configurable: true
});
EventListener.prototype.invokeWithEvent = function(event) {
try {
this.method.call(this.controller, event);
} catch (error) {
this.context.handleError(error, 'invoking action "' + this.action + '"', {
event: event
});
}
};
EventListener.prototype.willBeInvokedByEvent = function(event) {
var eventTarget = event.target;
if (this.element === eventTarget) {
return true;
} else if (eventTarget instanceof Element && this.element.contains(eventTarget)) {
return this.scope.containsElement(eventTarget);
} else {
return true;
}
};
Object.defineProperty(EventListener.prototype, "controller", {
get: function() {
return this.context.controller;
},
enumerable: true,
configurable: true
});
Object.defineProperty(EventListener.prototype, "methodName", {
get: function() {
return this.action.methodName;
},
enumerable: true,
configurable: true
});
Object.defineProperty(EventListener.prototype, "element", {
get: function() {
return this.scope.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(EventListener.prototype, "scope", {
get: function() {
return this.context.scope;
},
enumerable: true,
configurable: true
});
return EventListener;
}();
var EventListenerSet = function() {
function EventListenerSet(context) {
this.context = context;
this.started = false;
this.eventListenersByAction = new Map();
}
EventListenerSet.prototype.start = function() {
if (!this.started) {
this.started = true;
this.connectEventListeners();
}
};
EventListenerSet.prototype.stop = function() {
if (this.started) {
this.disconnectEventListeners();
this.started = false;
}
};
Object.defineProperty(EventListenerSet.prototype, "eventListeners", {
get: function() {
return Array.from(this.eventListenersByAction.values());
},
enumerable: true,
configurable: true
});
EventListenerSet.prototype.addEventListenerForAction = function(action) {
if (!this.eventListenersByAction.has(action)) {
var eventListener = new EventListener(this.context, action);
this.eventListenersByAction.set(action, eventListener);
if (this.started) eventListener.connect();
}
};
EventListenerSet.prototype.deleteEventListenerForAction = function(action) {
var eventListener = this.eventListenersByAction.get(action);
if (eventListener) {
this.eventListenersByAction.delete(action);
eventListener.disconnect();
}
};
EventListenerSet.prototype.connectEventListeners = function() {
this.eventListeners.forEach(function(eventListener) {
return eventListener.connect();
});
};
EventListenerSet.prototype.disconnectEventListeners = function() {
this.eventListeners.forEach(function(eventListener) {
return eventListener.disconnect();
});
};
return EventListenerSet;
}();
var Context = function() {
function Context(module, scope) {
this.module = module;
this.scope = scope;
this.actionObserver = new ActionObserver(this.element, this.schema, this);
this.eventListeners = new EventListenerSet(this);
try {
this.controller = new module.controllerConstructor(this);
this.controller.initialize();
} catch (error) {
this.handleError(error, "initializing controller");
}
}
Context.prototype.connect = function() {
this.actionObserver.start();
this.eventListeners.start();
try {
this.controller.connect();
} catch (error) {
this.handleError(error, "connecting controller");
}
};
Context.prototype.disconnect = function() {
try {
this.controller.disconnect();
} catch (error) {
this.handleError(error, "disconnecting controller");
}
this.eventListeners.stop();
this.actionObserver.stop();
};
Object.defineProperty(Context.prototype, "application", {
get: function() {
return this.module.application;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Context.prototype, "identifier", {
get: function() {
return this.module.identifier;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Context.prototype, "schema", {
get: function() {
return this.application.schema;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Context.prototype, "element", {
get: function() {
return this.scope.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Context.prototype, "parentElement", {
get: function() {
return this.element.parentElement;
},
enumerable: true,
configurable: true
});
Context.prototype.actionConnected = function(action) {
this.eventListeners.addEventListenerForAction(action);
};
Context.prototype.actionDisconnected = function(action) {
this.eventListeners.deleteEventListenerForAction(action);
};
Context.prototype.handleError = function(error, message, detail) {
if (detail === void 0) {
detail = {};
}
var _a = this, identifier = _a.identifier, controller = _a.controller, element = _a.element;
detail = Object.assign({
identifier: identifier,
controller: controller,
element: element
}, detail);
this.application.handleError(error, "Error " + message, detail);
};
return Context;
}();
var __extends$1 = undefined && undefined.__extends || function() {
var extendStatics = Object.setPrototypeOf || {
__proto__: []
} instanceof Array && function(d, b) {
d.__proto__ = b;
} || function(d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
};
return function(d, b) {
extendStatics(d, b);
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
}();
function blessDefinition(definition) {
return {
identifier: definition.identifier,
controllerConstructor: blessControllerConstructor(definition.controllerConstructor)
};
}
function blessControllerConstructor(controllerConstructor) {
var constructor = extend(controllerConstructor);
constructor.bless();
return constructor;
}
var extend = function() {
function extendWithReflect(constructor) {
function Controller() {
var _newTarget = this && this instanceof Controller ? this.constructor : void 0;
return Reflect.construct(constructor, arguments, _newTarget);
}
Controller.prototype = Object.create(constructor.prototype, {
constructor: {
value: Controller
}
});
Reflect.setPrototypeOf(Controller, constructor);
return Controller;
}
function testReflectExtension() {
var a = function() {
this.a.call(this);
};
var b = extendWithReflect(a);
b.prototype.a = function() {};
return new b();
}
try {
testReflectExtension();
return extendWithReflect;
} catch (error) {
return function(constructor) {
return function(_super) {
__extends$1(Controller, _super);
function Controller() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Controller;
}(constructor);
};
}
}();
var Module = function() {
function Module(application, definition) {
this.application = application;
this.definition = blessDefinition(definition);
this.contextsByScope = new WeakMap();
this.connectedContexts = new Set();
}
Object.defineProperty(Module.prototype, "identifier", {
get: function() {
return this.definition.identifier;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Module.prototype, "controllerConstructor", {
get: function() {
return this.definition.controllerConstructor;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Module.prototype, "contexts", {
get: function() {
return Array.from(this.connectedContexts);
},
enumerable: true,
configurable: true
});
Module.prototype.connectContextForScope = function(scope) {
var context = this.fetchContextForScope(scope);
this.connectedContexts.add(context);
context.connect();
};
Module.prototype.disconnectContextForScope = function(scope) {
var context = this.contextsByScope.get(scope);
if (context) {
this.connectedContexts.delete(context);
context.disconnect();
}
};
Module.prototype.fetchContextForScope = function(scope) {
var context = this.contextsByScope.get(scope);
if (!context) {
context = new Context(this, scope);
this.contextsByScope.set(scope, context);
}
return context;
};
return Module;
}();
var DataMap = function() {
function DataMap(scope) {
this.scope = scope;
}
Object.defineProperty(DataMap.prototype, "element", {
get: function() {
return this.scope.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DataMap.prototype, "identifier", {
get: function() {
return this.scope.identifier;
},
enumerable: true,
configurable: true
});
DataMap.prototype.get = function(key) {
key = this.getFormattedKey(key);
return this.element.getAttribute(key);
};
DataMap.prototype.set = function(key, value) {
key = this.getFormattedKey(key);
this.element.setAttribute(key, value);
return this.get(key);
};
DataMap.prototype.has = function(key) {
key = this.getFormattedKey(key);
return this.element.hasAttribute(key);
};
DataMap.prototype.delete = function(key) {
if (this.has(key)) {
key = this.getFormattedKey(key);
this.element.removeAttribute(key);
return true;
} else {
return false;
}
};
DataMap.prototype.getFormattedKey = function(key) {
return "data-" + this.identifier + "-" + dasherize(key);
};
return DataMap;
}();
function dasherize(value) {
return value.toString().replace(/([A-Z])/g, function(_, char) {
return "-" + char.toLowerCase();
});
}
function attributeValueContainsToken(attributeName, token) {
return "[" + attributeName + '~="' + token + '"]';
}
var TargetSet = function() {
function TargetSet(scope) {
this.scope = scope;
}
Object.defineProperty(TargetSet.prototype, "element", {
get: function() {
return this.scope.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TargetSet.prototype, "identifier", {
get: function() {
return this.scope.identifier;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TargetSet.prototype, "schema", {
get: function() {
return this.scope.schema;
},
enumerable: true,
configurable: true
});
TargetSet.prototype.has = function(targetName) {
return this.find(targetName) != null;
};
TargetSet.prototype.find = function() {
var targetNames = [];
for (var _i = 0; _i < arguments.length; _i++) {
targetNames[_i] = arguments[_i];
}
var selector = this.getSelectorForTargetNames(targetNames);
return this.scope.findElement(selector);
};
TargetSet.prototype.findAll = function() {
var targetNames = [];
for (var _i = 0; _i < arguments.length; _i++) {
targetNames[_i] = arguments[_i];
}
var selector = this.getSelectorForTargetNames(targetNames);
return this.scope.findAllElements(selector);
};
TargetSet.prototype.getSelectorForTargetNames = function(targetNames) {
var _this = this;
return targetNames.map(function(targetName) {
return _this.getSelectorForTargetName(targetName);
}).join(", ");
};
TargetSet.prototype.getSelectorForTargetName = function(targetName) {
var targetDescriptor = this.identifier + "." + targetName;
return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor);
};
return TargetSet;
}();
var Scope = function() {
function Scope(schema, identifier, element) {
this.schema = schema;
this.identifier = identifier;
this.element = element;
this.targets = new TargetSet(this);
this.data = new DataMap(this);
}
Scope.prototype.findElement = function(selector) {
return this.findAllElements(selector)[0];
};
Scope.prototype.findAllElements = function(selector) {
var head = this.element.matches(selector) ? [ this.element ] : [];
var tail = this.filterElements(Array.from(this.element.querySelectorAll(selector)));
return head.concat(tail);
};
Scope.prototype.filterElements = function(elements) {
var _this = this;
return elements.filter(function(element) {
return _this.containsElement(element);
});
};
Scope.prototype.containsElement = function(element) {
return element.closest(this.controllerSelector) === this.element;
};
Object.defineProperty(Scope.prototype, "controllerSelector", {
get: function() {
return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier);
},
enumerable: true,
configurable: true
});
return Scope;
}();
var ScopeObserver = function() {
function ScopeObserver(element, schema, delegate) {
this.element = element;
this.schema = schema;
this.delegate = delegate;
this.tokenObserver = new TokenObserver(this.element, this.controllerAttribute, this);
}
ScopeObserver.prototype.start = function() {
this.tokenObserver.start();
};
ScopeObserver.prototype.stop = function() {
this.tokenObserver.stop();
};
Object.defineProperty(ScopeObserver.prototype, "controllerAttribute", {
get: function() {
return this.schema.controllerAttribute;
},
enumerable: true,
configurable: true
});
ScopeObserver.prototype.parseValueFromTokenSource = function(source) {
return new Scope(this.schema, source.value, source.element);
};
ScopeObserver.prototype.elementMatchedToken = function(token) {
this.delegate.scopeConnected(token.value);
};
ScopeObserver.prototype.elementUnmatchedToken = function(token) {
this.delegate.scopeDisconnected(token.value);
};
return ScopeObserver;
}();
var Router = function() {
function Router(application) {
this.application = application;
this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
this.scopesByIdentifier = new Multimap();
this.modulesByIdentifier = new Map();
}
Object.defineProperty(Router.prototype, "element", {
get: function() {
return this.application.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Router.prototype, "schema", {
get: function() {
return this.application.schema;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Router.prototype, "controllerAttribute", {
get: function() {
return this.schema.controllerAttribute;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Router.prototype, "modules", {
get: function() {
return Array.from(this.modulesByIdentifier.values());
},
enumerable: true,
configurable: true
});
Object.defineProperty(Router.prototype, "contexts", {
get: function() {
return this.modules.reduce(function(contexts, module) {
return contexts.concat(module.contexts);
}, []);
},
enumerable: true,
configurable: true
});
Router.prototype.start = function() {
this.scopeObserver.start();
};
Router.prototype.stop = function() {
this.scopeObserver.stop();
};
Router.prototype.loadDefinition = function(definition) {
this.unloadIdentifier(definition.identifier);
var module = new Module(this.application, definition);
this.connectModule(module);
};
Router.prototype.unloadIdentifier = function(identifier) {
var module = this.modulesByIdentifier.get(identifier);
if (module) {
this.disconnectModule(module);
}
};
Router.prototype.getContextForElementAndIdentifier = function(element, identifier) {
var module = this.modulesByIdentifier.get(identifier);
if (module) {
return module.contexts.find(function(context) {
return context.element == element;
});
}
};
Router.prototype.handleError = function(error, message, detail) {
this.application.handleError(error, message, detail);
};
Router.prototype.scopeConnected = function(scope) {
this.scopesByIdentifier.add(scope.identifier, scope);
var module = this.modulesByIdentifier.get(scope.identifier);
if (module) {
module.connectContextForScope(scope);
}
};
Router.prototype.scopeDisconnected = function(scope) {
this.scopesByIdentifier.delete(scope.identifier, scope);
var module = this.modulesByIdentifier.get(scope.identifier);
if (module) {
module.disconnectContextForScope(scope);
}
};
Router.prototype.connectModule = function(module) {
this.modulesByIdentifier.set(module.identifier, module);
var scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
scopes.forEach(function(scope) {
return module.connectContextForScope(scope);
});
};
Router.prototype.disconnectModule = function(module) {
this.modulesByIdentifier.delete(module.identifier);
var scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
scopes.forEach(function(scope) {
return module.disconnectContextForScope(scope);
});
};
return Router;
}();
var defaultSchema = {
controllerAttribute: "data-controller",
actionAttribute: "data-action",
targetAttribute: "data-target"
};
var __awaiter = undefined && undefined.__awaiter || function(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : new P(function(resolve) {
resolve(result.value);
}).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = undefined && undefined.__generator || function(thisArg, body) {
var _ = {
label: 0,
sent: function() {
if (t[0] & 1) throw t[1];
return t[1];
},
trys: [],
ops: []
}, f, y, t, g;
return g = {
next: verb(0),
throw: verb(1),
return: verb(2)
}, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
return this;
}), g;
function verb(n) {
return function(v) {
return step([ n, v ]);
};
}
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [ 0, t.value ];
switch (op[0]) {
case 0:
case 1:
t = op;
break;
case 4:
_.label++;
return {
value: op[1],
done: false
};
case 5:
_.label++;
y = op[1];
op = [ 0 ];
continue;
case 7:
op = _.ops.pop();
_.trys.pop();
continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
_ = 0;
continue;
}
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
_.label = op[1];
break;
}
if (op[0] === 6 && _.label < t[1]) {
_.label = t[1];
t = op;
break;
}
if (t && _.label < t[2]) {
_.label = t[2];
_.ops.push(op);
break;
}
if (t[2]) _.ops.pop();
_.trys.pop();
continue;
}
op = body.call(thisArg, _);
} catch (e) {
op = [ 6, e ];
y = 0;
} finally {
f = t = 0;
}
if (op[0] & 5) throw op[1];
return {
value: op[0] ? op[1] : void 0,
done: true
};
}
};
var Application = function() {
function Application(element, schema) {
if (element === void 0) {
element = document.documentElement;
}
if (schema === void 0) {
schema = defaultSchema;
}
this.element = element;
this.schema = schema;
this.router = new Router(this);
}
Application.start = function(element, schema) {
var application = new Application(element, schema);
application.start();
return application;
};
Application.prototype.start = function() {
return __awaiter(this, void 0, void 0, function() {
return __generator(this, function(_a) {
switch (_a.label) {
case 0:
return [ 4, domReady() ];
case 1:
_a.sent();
this.router.start();
return [ 2 ];
}
});
});
};
Application.prototype.stop = function() {
this.router.stop();
};
Application.prototype.register = function(identifier, controllerConstructor) {
this.load({
identifier: identifier,
controllerConstructor: controllerConstructor
});
};
Application.prototype.load = function(head) {
var _this = this;
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
var definitions = Array.isArray(head) ? head : [ head ].concat(rest);
definitions.forEach(function(definition) {
return _this.router.loadDefinition(definition);
});
};
Application.prototype.unload = function(head) {
var _this = this;
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
var identifiers = Array.isArray(head) ? head : [ head ].concat(rest);
identifiers.forEach(function(identifier) {
return _this.router.unloadIdentifier(identifier);
});
};
Object.defineProperty(Application.prototype, "controllers", {
get: function() {
return this.router.contexts.map(function(context) {
return context.controller;
});
},
enumerable: true,
configurable: true
});
Application.prototype.getControllerForElementAndIdentifier = function(element, identifier) {
var context = this.router.getContextForElementAndIdentifier(element, identifier);
return context ? context.controller : null;
};
Application.prototype.handleError = function(error, message, detail) {
console.error("%s\n\n%o\n\n%o", message, error, detail);
};
return Application;
}();
function domReady() {
return new Promise(function(resolve) {
if (document.readyState == "loading") {
document.addEventListener("DOMContentLoaded", resolve);
} else {
resolve();
}
});
}
function defineTargetProperties(constructor) {
var prototype = constructor.prototype;
var targetNames = getTargetNamesForConstructor(constructor);
targetNames.forEach(function(name) {
return defineLinkedProperties(prototype, (_a = {}, _a[name + "Target"] = {
get: function() {
var target = this.targets.find(name);
if (target) {
return target;
} else {
throw new Error('Missing target element "' + this.identifier + "." + name + '"');
}
}
}, _a[name + "Targets"] = {
get: function() {
return this.targets.findAll(name);
}
}, _a["has" + capitalize(name) + "Target"] = {
get: function() {
return this.targets.has(name);
}
}, _a));
var _a;
});
}
function getTargetNamesForConstructor(constructor) {
var ancestors = getAncestorsForConstructor(constructor);
return Array.from(ancestors.reduce(function(targetNames, constructor) {
getOwnTargetNamesForConstructor(constructor).forEach(function(name) {
return targetNames.add(name);
});
return targetNames;
}, new Set()));
}
function getAncestorsForConstructor(constructor) {
var ancestors = [];
while (constructor) {
ancestors.push(constructor);
constructor = Object.getPrototypeOf(constructor);
}
return ancestors;
}
function getOwnTargetNamesForConstructor(constructor) {
var definition = constructor["targets"];
return Array.isArray(definition) ? definition : [];
}
function defineLinkedProperties(object, properties) {
Object.keys(properties).forEach(function(name) {
if (!(name in object)) {
var descriptor = properties[name];
Object.defineProperty(object, name, descriptor);
}
});
}
function capitalize(name) {
return name.charAt(0).toUpperCase() + name.slice(1);
}
var Controller = function() {
function Controller(context) {
this.context = context;
}
Controller.bless = function() {
defineTargetProperties(this);
};
Object.defineProperty(Controller.prototype, "application", {
get: function() {
return this.context.application;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Controller.prototype, "scope", {
get: function() {
return this.context.scope;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Controller.prototype, "element", {
get: function() {
return this.scope.element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Controller.prototype, "identifier", {
get: function() {
return this.scope.identifier;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Controller.prototype, "targets", {
get: function() {
return this.scope.targets;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Controller.prototype, "data", {
get: function() {
return this.scope.data;
},
enumerable: true,
configurable: true
});
Controller.prototype.initialize = function() {};
Controller.prototype.connect = function() {};
Controller.prototype.disconnect = function() {};
Controller.targets = [];
return Controller;
}();
exports.Application = Application;
exports.Context = Context;
exports.Controller = Controller;
exports.defaultSchema = defaultSchema;
Object.defineProperty(exports, "__esModule", {
value: true
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment