@kospa/base
Version:
Base components for kospa framework
247 lines (246 loc) • 10.5 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
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 extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "knockout", "./loader", "./system", "./activator"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compose = exports.CompositionError = void 0;
var ko = require("knockout");
var loader = require("./loader");
var system = require("./system");
var activator = require("./activator");
var VIEW_CACHE = {};
var CompositionError = /** @class */ (function (_super) {
__extends(CompositionError, _super);
function CompositionError(vm, innerError) {
var _this = _super.call(this, "composer: " + (innerError ? innerError : "Unknown")) || this;
_this.vm = vm;
_this.innerError = innerError;
if (innerError && typeof innerError.stack !== "undefined") {
Object.defineProperty(_this, "stack", { get: function () { return innerError.stack; } });
}
return _this;
}
return CompositionError;
}(Error));
exports.CompositionError = CompositionError;
/**
* Compose a ViewModel and a View into an element using Require.JS.
* @param element - HTMLElement to compose on or its ID.
* @param options - Composition Options.
*/
function compose(element, options) {
var node = typeof element === "string" ?
document.getElementById(element) :
element;
if (!node) {
throw new CompositionError(options.viewmodel, "Can't find element: " + element);
}
return loadComponents(options)
.then(function (options) { return activation(node, options); })
.catch(function (err) {
if (err instanceof CompositionError) {
throw err;
}
throw new CompositionError(options.viewmodel, err);
})
.then(function () { return node; });
}
exports.compose = compose;
//#endregion
//#region Knockout Handlers
ko.bindingHandlers["compose"] = {
init: function () {
return { controlsDescendantBindings: true };
},
update: function (element, valueAccessor) {
compose(element, ko.toJS(valueAccessor()))
.catch(system.error);
}
};
ko.virtualElements.allowedBindings["compose"] = true;
ko.components.register("kospa-compose", { template: "<!--ko compose: $data--><!--/ko-->" });
//#endregion
//#region Loading Methods
function loadComponents(options) {
if (typeof options.viewmodel === "string" && !options.view) {
options.view = options.viewmodel;
}
return loadViewModel(options.viewmodel)
.then(function (vm) {
if (!vm) {
throw new CompositionError(options.viewmodel, "ViewModel module can't be empty!");
}
options.viewmodel = vm;
})
.then(function () { return loadView(options.view, options.viewmodel, options.args); })
.then(function (view) { options.view = view; })
.then(function () { return options; });
}
function loadViewModel(viewmodel) {
return typeof viewmodel === "string" ?
loader.loadModule(viewmodel) :
Promise.resolve(viewmodel);
}
function loadView(view, vm, args) {
if (vm && typeof vm.getView === "function") {
view = vm.getView.apply(vm, (args || [])) || view;
}
if (!view) {
return Promise.reject(new CompositionError(vm, "No view is provided!"));
}
return parseView(view);
}
function parseView(view) {
if (typeof view === "string") {
if (VIEW_CACHE[view]) {
return Promise.resolve(VIEW_CACHE[view]);
}
if (view.indexOf("<") === -1) {
return loader.loadView(view)
.then(function (tpl) { return parseView(tpl); })
.then(function (tpl) { return VIEW_CACHE[view] = tpl; });
}
return Promise.resolve(ko.utils.parseHtmlFragment(view))
.then(function (tpl) { return VIEW_CACHE[view] = tpl; });
}
if (Array.isArray(view)) {
return Promise.resolve(view);
}
if (isDocumentFragment(view)) {
return Promise.resolve(arrayFromNodeList(view.childNodes));
}
throw new Error("Unknown view value: " + view);
}
//#endregion
//#region Activation Methods
function activation(node, options) {
if (!options.activate) {
var oldVm = ko.utils.domData.get(node, "kospa_vm"), vm = activator.constructs(options.viewmodel);
return applyBindings(node, oldVm, vm, options);
}
return deactivateNode(node, options.viewmodel)
.then(function (oldVm) { return activateNode(node, oldVm, options); });
}
function activateNode(node, oldVm, options) {
return activator.activate(options.viewmodel, options.args)
.then(function (vm) { return applyBindings(node, oldVm, vm, options); });
}
function deactivateNode(node, newVm) {
var oldVm = ko.utils.domData.get(node, "kospa_vm");
return activator.deactivate(oldVm, newVm);
}
//#endregion
//#region Binding Methods
ko.bindingHandlers["internal_compose"] = {
init: function (node, valueAccessor, allBindings, vm, bindingContext) {
var _a = valueAccessor(), oldVm = _a.oldVm, options = _a.options, resolve = _a.resolve, reject = _a.reject;
if (oldVm === vm) {
return { controlsDescendantBindings: true };
}
if (!oldVm) {
ko.utils.domNodeDisposal.addDisposeCallback(node, dispose);
}
clean(node);
moveNodes(options.view, node);
var ctx = ko.bindingEvent.startPossiblyAsyncContentBinding(node, bindingContext);
var sub = ko.bindingEvent.subscribe(node, "descendantsComplete", onDescendantsComplete, vm);
ko.utils.domData.set(node, "kospa_complete", sub);
ko.applyBindingsToDescendants(ctx, node);
ko.utils.domData.set(node, "kospa_vm", vm);
bindingComplete(node, vm, options);
return { controlsDescendantBindings: true };
function onDescendantsComplete() {
descendantsComplete(node, vm, options)
.then(resolve, reject);
}
}
};
ko.virtualElements.allowedBindings["internal_compose"] = true;
function applyBindings(node, oldVm, vm, options) {
return new Promise(function (resolve, reject) {
ko.applyBindingAccessorsToNode(node, { internal_compose: function () { return ({ oldVm: oldVm, options: options, resolve: resolve, reject: reject }); } }, vm);
});
}
function dispose(node) {
var sub = ko.utils.domData.get(node, "kospa_complete");
if (sub)
sub.dispose();
var vm = ko.utils.domData.get(node, "kospa_vm");
if (!vm)
return;
activator.deactivate(vm);
activator.call(vm, "dispose");
}
function clean(node) {
var sub = ko.utils.domData.get(node, "kospa_complete");
if (sub)
sub.dispose();
ko.virtualElements.emptyNode(node);
}
function moveNodes(nodes, dest) {
ko.virtualElements.setDomNodeChildren(dest, cloneNodes(nodes));
}
function cloneNodes(nodes) {
return nodes.map(function (node) { return node.cloneNode(true); });
}
function arrayFromNodeList(nodes) {
return Array.prototype.slice.call(nodes);
}
function isDocumentFragment(obj) {
return typeof DocumentFragment !== "undefined" ?
obj instanceof DocumentFragment :
obj && obj.nodeType === 11;
}
//#endregion
//#region Callback Methods
function descendantsComplete(node, vm, options) {
var sub = ko.utils.domData.get(node, "kospa_complete");
if (sub)
sub.dispose();
var args = options.args || [];
return Promise.all([
activator.call.apply(activator, __spreadArrays([vm, "descendantsComplete", node], args)),
activator.call.apply(activator, __spreadArrays([options, "descendantsComplete", node, vm], args))
]).then(function () { return compositionComplete(vm, options); });
}
function bindingComplete(node, vm, options) {
var args = options.args || [];
return Promise.all([
activator.call.apply(activator, __spreadArrays([vm, "bindingComplete", node], args)),
activator.call.apply(activator, __spreadArrays([options, "bindingComplete", node, vm], args))
]).then(function () { return void 0; });
}
function compositionComplete(vm, options) {
var args = options.args || [];
return Promise.all([
activator.call.apply(activator, __spreadArrays([vm, "compositionComplete"], args)),
activator.call.apply(activator, __spreadArrays([options, "compositionComplete", vm], args))
]).then(function () { return void 0; });
}
});