mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-29 20:19:22 +03:00
87 lines
4.9 KiB
JavaScript
87 lines
4.9 KiB
JavaScript
(function (undefined) {
|
|
// Overridable API for determining which component name applies to a given node. By overriding this,
|
|
// you can for example map specific tagNames to components that are not preregistered.
|
|
ko.components['getComponentNameForNode'] = function(node) {
|
|
var tagNameLower = ko.utils.tagNameLower(node);
|
|
if (ko.components.isRegistered(tagNameLower)) {
|
|
// Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
|
|
if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]") {
|
|
return tagNameLower;
|
|
}
|
|
}
|
|
};
|
|
|
|
ko.components.addBindingsForCustomElement = function(allBindings, node, bindingContext, valueAccessors) {
|
|
// Determine if it's really a custom element matching a component
|
|
if (node.nodeType === 1) {
|
|
var componentName = ko.components['getComponentNameForNode'](node);
|
|
if (componentName) {
|
|
// It does represent a component, so add a component binding for it
|
|
allBindings = allBindings || {};
|
|
|
|
if (allBindings['component']) {
|
|
// Avoid silently overwriting some other 'component' binding that may already be on the element
|
|
throw new Error('Cannot use the "component" binding on a custom element matching a component');
|
|
}
|
|
|
|
var componentBindingValue = { 'name': componentName, 'params': getComponentParamsFromCustomElement(node, bindingContext) };
|
|
|
|
allBindings['component'] = valueAccessors
|
|
? function() { return componentBindingValue; }
|
|
: componentBindingValue;
|
|
}
|
|
}
|
|
|
|
return allBindings;
|
|
}
|
|
|
|
var nativeBindingProviderInstance = new ko.bindingProvider();
|
|
|
|
function getComponentParamsFromCustomElement(elem, bindingContext) {
|
|
var paramsAttribute = elem.getAttribute('params');
|
|
|
|
if (paramsAttribute) {
|
|
var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),
|
|
rawParamComputedValues = ko.utils.objectMap(params, function(paramValue, paramName) {
|
|
return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
|
|
}),
|
|
result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {
|
|
var paramValue = paramValueComputed.peek();
|
|
// Does the evaluation of the parameter value unwrap any observables?
|
|
if (!paramValueComputed.isActive()) {
|
|
// No it doesn't, so there's no need for any computed wrapper. Just pass through the supplied value directly.
|
|
// Example: "someVal: firstName, age: 123" (whether or not firstName is an observable/computed)
|
|
return paramValue;
|
|
} else {
|
|
// Yes it does. Supply a computed property that unwraps both the outer (binding expression)
|
|
// level of observability, and any inner (resulting model value) level of observability.
|
|
// This means the component doesn't have to worry about multiple unwrapping. If the value is a
|
|
// writable observable, the computed will also be writable and pass the value on to the observable.
|
|
return ko.computed({
|
|
'read': function() {
|
|
return ko.utils.unwrapObservable(paramValueComputed());
|
|
},
|
|
'write': ko.isWriteableObservable(paramValue) && function(value) {
|
|
paramValueComputed()(value);
|
|
},
|
|
disposeWhenNodeIsRemoved: elem
|
|
});
|
|
}
|
|
});
|
|
|
|
// Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called '$raw'
|
|
// This is in case the developer wants to react to outer (binding) observability separately from inner
|
|
// (model value) observability, or in case the model value observable has subobservables.
|
|
if (!Object.prototype.hasOwnProperty.call(result, '$raw')) {
|
|
result['$raw'] = rawParamComputedValues;
|
|
}
|
|
|
|
return result;
|
|
} else {
|
|
// For consistency, absence of a "params" attribute is treated the same as the presence of
|
|
// any empty one. Otherwise component viewmodels need special code to check whether or not
|
|
// 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.
|
|
return { '$raw': {} };
|
|
}
|
|
}
|
|
})();
|