hy-push-state
Version:
Turn static web sites into dynamic web apps
174 lines (139 loc) • 9.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.eventListenersMixin = void 0;
var _rxjs = require("hy-component/src/rxjs");
var _common = require("../common");
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var eventListenersMixin = function eventListenersMixin(C) {
return /*#__PURE__*/function (_C) {
_inherits(_class, _C);
var _super = _createSuper(_class);
function _class() {
_classCallCheck(this, _class);
return _super.apply(this, arguments);
}
_createClass(_class, [{
key: "setupEventListeners",
value: function setupEventListeners() {
var _this = this;
// #### Keeping track of links
// We use a `MutationObserver` to keep track of all the links inside the component,
// and put events on the `pushSubject` and `hintSubject` observables,
// but first we need to check if `MutationObserver` is available.
if ("MutationObserver" in window && "WeakSet" in window) {
// A `Set` of `Element`s.
// We use this to keep track of which links already have their event listeners registered.
var links = new WeakSet(); // Binding `next` functions to their `Subject`s,
// so that we can pass them as callbacks directly. This is just for convenience.
var pushNext = this.pushSubject.next.bind(this.pushSubject);
var hintNext = this.hintSubject.next.bind(this.hintSubject); // We don't use `Observable.fromEvent` here to avoid creating too many observables.
// Registering an unknown number of event listeners is somewhat debatable,
// but we certainly don't want to make it wrose.
// (The number could be brought down by using an `IntersectionObserver` in the future.
// Also note that typically there will be an animation playing while this is happening,
// so the effects are not easily noticed).
//
// In any case, `MutationObserver` and `Set` help us keep track of which links are children
// of this component, so that we can reliably add and remove the event listeners.
// The function to be called for every added node:
var addLink = function addLink(link) {
if (!links.has(link)) {
links.add(link);
link.addEventListener("click", pushNext);
link.addEventListener("mouseenter", hintNext, {
passive: true
});
link.addEventListener("touchstart", hintNext, {
passive: true
});
link.addEventListener("focus", hintNext, {
passive: true
}); // When fetching resources from an external domain, rewrite the link's href,
// so that shift-click, etc works as expected.
// if (isExternal(this)) {
// link.href = new URL(link.getAttribute("href"), this.href).href;
// }
}
};
var addListeners = function addListeners(addedNode) {
if (addedNode instanceof Element) {
if (_common.matches.call(addedNode, _this.linkSelector)) {
addLink(addedNode);
} else {
Array.from(addedNode.querySelectorAll(_this.linkSelector)).forEach(addLink);
}
}
}; // Next, The function to be called for every removed node.
// Usually the elments will be removed from the document altogher
// when they are removed from this component,
// but since we can't be sure, we remove the event listeners anyway.
var removeLink = function removeLink(link) {
links["delete"](link);
link.removeEventListener("click", pushNext);
link.removeEventListener("mouseenter", hintNext, {
passive: true
});
link.removeEventListener("touchstart", hintNext, {
passive: true
});
link.removeEventListener("focus", hintNext, {
passive: true
});
};
var removeListeners = function removeListeners(removedNode) {
if (removedNode instanceof Element) {
if (_common.matches.call(removedNode, _this.linkSelector)) {
removeLink(removedNode);
} else {
Array.from(removedNode.querySelectorAll(_this.linkSelector)).forEach(removeLink);
}
}
}; // An observable wrapper around the mutation observer.
// We're only interested in nodes entering and leaving the entire subtree of this component,
// but not attribute changes.
var mutation$ = (0, _rxjs.createXObservable)(MutationObserver)(this.el, {}, {
childList: true,
subtree: true
}); // For every mutation, we remove the event listeners of elements that go out of the component
// (if any), and add event listeners to all elements that make it into the compnent (if any).
mutation$.subscribe(function (_ref) {
var addedNodes = _ref.addedNodes,
removedNodes = _ref.removedNodes;
Array.from(removedNodes).forEach(removeListeners.bind(_this));
Array.from(addedNodes).forEach(addListeners.bind(_this));
}); // TODO
this.subjects.linkSelector.subscribe(function () {
// TODO
Array.from(links).forEach(removeLink); // The mutation observer does not pick up the links that are already on the page,
// so we add them manually here, once.
addListeners.call(_this, _this.el);
}); // If we don't have `MutationObserver` and `Set`, we just register a `click` event listener
// on the entire component, and check if a click occurred on one of our links.
// Note that we can't reliably generate hints this way, so we don't.
} else {
this.el.addEventListener("click", function (event) {
var anchor = _common.matchesAncestors.call(event.target, _this.linkSelector);
if (anchor && anchor.href) {
event.currentTarget = anchor; // eslint-disable-line no-param-reassign
pushSubject.next(event);
}
});
}
}
}]);
return _class;
}(C);
};
exports.eventListenersMixin = eventListenersMixin;