@cycle/dom
Version:
The standard DOM Driver for Cycle.js, based on Snabbdom
138 lines • 5.66 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
var snabbdom_1 = require("snabbdom");
var xstream_1 = require("xstream");
var concat_1 = require("xstream/extra/concat");
var sampleCombine_1 = require("xstream/extra/sampleCombine");
var MainDOMSource_1 = require("./MainDOMSource");
var VNodeWrapper_1 = require("./VNodeWrapper");
var utils_1 = require("./utils");
var modules_1 = require("./modules");
var IsolateModule_1 = require("./IsolateModule");
var EventDelegator_1 = require("./EventDelegator");
function makeDOMDriverInputGuard(modules) {
if (!Array.isArray(modules)) {
throw new Error("Optional modules option must be an array for snabbdom modules");
}
}
function domDriverInputGuard(view$) {
if (!view$ ||
typeof view$.addListener !== "function" ||
typeof view$.fold !== "function") {
throw new Error("The DOM driver function expects as input a Stream of " +
"virtual DOM elements");
}
}
function dropCompletion(input) {
return xstream_1.default.merge(input, xstream_1.default.never());
}
function unwrapElementFromVNode(vnode) {
return vnode.elm;
}
function defaultReportSnabbdomError(err) {
(console.error || console.log)(err);
}
function makeDOMReady$() {
return xstream_1.default.create({
start: function (lis) {
if (document.readyState === 'loading') {
document.addEventListener('readystatechange', function () {
var state = document.readyState;
if (state === 'interactive' || state === 'complete') {
lis.next(null);
lis.complete();
}
});
}
else {
lis.next(null);
lis.complete();
}
},
stop: function () { },
});
}
function addRootScope(vnode) {
vnode.data = vnode.data || {};
vnode.data.isolate = [];
return vnode;
}
function makeDOMDriver(container, options) {
if (options === void 0) { options = {}; }
utils_1.checkValidContainer(container);
var modules = options.modules || modules_1.default;
makeDOMDriverInputGuard(modules);
var isolateModule = new IsolateModule_1.IsolateModule();
var snabbdomOptions = options && options.snabbdomOptions || undefined;
var patch = snabbdom_1.init([isolateModule.createModule()].concat(modules), undefined, snabbdomOptions);
var domReady$ = makeDOMReady$();
var vnodeWrapper;
var mutationObserver;
var mutationConfirmed$ = xstream_1.default.create({
start: function (listener) {
mutationObserver = new MutationObserver(function () { return listener.next(null); });
},
stop: function () {
mutationObserver.disconnect();
},
});
function DOMDriver(vnode$, name) {
if (name === void 0) { name = 'DOM'; }
domDriverInputGuard(vnode$);
var sanitation$ = xstream_1.default.create();
var firstRoot$ = domReady$.map(function () {
var firstRoot = utils_1.getValidNode(container) || document.body;
vnodeWrapper = new VNodeWrapper_1.VNodeWrapper(firstRoot);
return firstRoot;
});
// We need to subscribe to the sink (i.e. vnode$) synchronously inside this
// driver, and not later in the map().flatten() because this sink is in
// reality a SinkProxy from @cycle/run, and we don't want to miss the first
// emission when the main() is connected to the drivers.
// Read more in issue #739.
var rememberedVNode$ = vnode$.remember();
rememberedVNode$.addListener({});
// The mutation observer internal to mutationConfirmed$ should
// exist before elementAfterPatch$ calls mutationObserver.observe()
mutationConfirmed$.addListener({});
var elementAfterPatch$ = firstRoot$
.map(function (firstRoot) {
return xstream_1.default
.merge(rememberedVNode$.endWhen(sanitation$), sanitation$)
.map(function (vnode) { return vnodeWrapper.call(vnode); })
.startWith(addRootScope(snabbdom_1.toVNode(firstRoot)))
.fold(patch, snabbdom_1.toVNode(firstRoot))
.drop(1)
.map(unwrapElementFromVNode)
.startWith(firstRoot)
.map(function (el) {
mutationObserver.observe(el, {
childList: true,
attributes: true,
characterData: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true,
});
return el;
})
.compose(dropCompletion);
} // don't complete this stream
)
.flatten();
var rootElement$ = concat_1.default(domReady$, mutationConfirmed$)
.endWhen(sanitation$)
.compose(sampleCombine_1.default(elementAfterPatch$))
.map(function (arr) { return arr[1]; })
.remember();
// Start the snabbdom patching, over time
rootElement$.addListener({
error: options.reportSnabbdomError || defaultReportSnabbdomError,
});
var delegator = new EventDelegator_1.EventDelegator(rootElement$, isolateModule);
return new MainDOMSource_1.MainDOMSource(rootElement$, sanitation$, [], isolateModule, delegator, name);
}
return DOMDriver;
}
exports.makeDOMDriver = makeDOMDriver;
//# sourceMappingURL=makeDOMDriver.js.map
;