angular-odata
Version:
Client side OData typescript library for Angular
816 lines • 127 kB
JavaScript
import { forkJoin, Observable, of, throwError } from 'rxjs';
import { defaultIfEmpty, finalize, map, switchMap } from 'rxjs/operators';
import { DEFAULT_VERSION } from '../constants';
import { ODataHelper } from '../helper';
import { ODataEntitySetResource, ODataNavigationPropertyResource, ODataPropertyResource, } from '../resources';
import { Types } from '../utils/types';
import { INCLUDE_DEEP, ODataModelEventEmitter, ODataModelEventType, ODataModelOptions, ODataModelState, } from './options';
import { ODataEntitiesAnnotations, ODataEntityAnnotations, } from '../annotations';
export class ODataCollection {
static model = null;
_parent = null;
_resource = null;
_resources = [];
_annotations;
_entries = [];
_model;
models() {
return this._entries
.filter((e) => e.state !== ODataModelState.Removed)
.map((e) => e.model);
}
get length() {
return this.models().length;
}
//Events
events$;
constructor(entities = [], { parent, resource, annots, model, reset = false, } = {}) {
const Klass = this.constructor;
if (!model && Klass.model !== null)
model = Klass.model;
if (!model)
throw new Error('Collection: Collection need model');
this._model = model;
// Events
this.events$ = new ODataModelEventEmitter({ collection: this });
this.events$.subscribe((e) => model.meta.events$.emit(e));
// Parent
if (parent !== undefined) {
this._parent = parent;
}
// Resource
if (this._parent === null && !resource)
resource = this._model.meta.collectionResourceFactory();
if (resource) {
this.attach(resource);
}
// Annotations
this._annotations =
annots ||
new ODataEntitiesAnnotations(ODataHelper[resource?.api?.options.version || DEFAULT_VERSION]);
entities = entities || [];
this.assign(entities, { reset });
}
isParentOf(child) {
return (child !== this &&
ODataModelOptions.chain(child).some((p) => p[0] === this));
}
resource() {
return ODataModelOptions.resource(this);
}
pushResource(resource) {
// Push current parent and resource
this._resources.push({ parent: this._parent, resource: this._resource });
// Replace parent and resource
this._parent = null;
this._resource = resource;
}
popResource() {
// Pop parent and resource
const pop = this._resources.pop();
if (pop !== undefined) {
const current = { parent: this._parent, resource: this._resource };
this._parent = pop.parent;
this._resource = pop.resource;
return current;
}
return undefined;
}
attach(resource) {
if (this._resource !== null &&
this._resource.outgoingType() !== resource.outgoingType() &&
!this._resource.isSubtypeOf(resource))
throw new Error(`attach: Can't reattach ${this._resource.outgoingType()} to ${resource.outgoingType()}`);
this._entries.forEach(({ model }) => {
const modelResource = this._model.meta.modelResourceFactory(resource.cloneQuery());
if (modelResource !== undefined)
model.attach(modelResource);
});
const current = this._resource;
if (current === null || !current.isEqualTo(resource)) {
this._resource = resource;
this.events$.trigger(ODataModelEventType.Attach, {
previous: current,
value: resource,
});
}
}
withResource(resource, ctx) {
// Push
this.pushResource(resource);
// Execute
const result = ctx(this);
if (result instanceof Observable) {
return result.pipe(finalize(() => this.popResource()));
}
else {
// Pop
this.popResource();
return result;
}
}
asEntitySet(ctx) {
// Build new resource
const resource = this._model.meta.collectionResourceFactory(this._resource?.cloneQuery());
return this.withResource(resource, ctx);
}
annots() {
return this._annotations;
}
modelFactory(data, { reset = false } = {}) {
let Model = this._model;
const annots = new ODataEntityAnnotations(this._annotations.helper);
annots.update(data);
if (annots?.type !== undefined && Model.meta !== null) {
const schema = Model.meta.findChildOptions((o) => o.isTypeOf(annots.type))?.structuredType;
if (schema !== undefined && schema.model !== undefined)
// Change to child model
Model = schema.model;
}
return new Model(data, {
annots,
reset,
parent: [this, null],
});
}
toEntities({ client_id = false, include_navigation = false, include_concurrency = false, include_computed = false, include_key = true, include_id = false, include_non_field = false, changes_only = false, field_mapping = false, chain = [], } = {}) {
return this._entries
.filter(({ model, state }) => state !== ODataModelState.Removed && chain.every((c) => c !== model))
.map(({ model, state }) => {
var changesOnly = changes_only && state !== ODataModelState.Added;
return model.toEntity({
client_id,
include_navigation,
include_concurrency,
include_computed,
include_key,
include_id,
include_non_field,
field_mapping,
changes_only: changesOnly,
chain: [this, ...chain],
});
});
}
toJson() {
return this.toEntities(INCLUDE_DEEP);
}
hasChanged({ include_navigation } = {}) {
return (this._entries.some((e) => e.state !== ODataModelState.Unchanged) ||
this.models().some((m) => m.hasChanged({ include_navigation })));
}
clone() {
return new this.constructor(this.toEntities(INCLUDE_DEEP), {
resource: this.resource(),
annots: this.annots(),
});
}
_request(obs$, mapCallback) {
this.events$.trigger(ODataModelEventType.Request, {
options: { observable: obs$ },
});
return obs$.pipe(map((response) => mapCallback(response)), finalize(() => this.events$.trigger(ODataModelEventType.Sync)));
}
fetch({ add, merge, remove, withCount, ...options } = {}) {
const resource = this.resource();
if (!resource)
return throwError(() => new Error('fetch: Resource is null'));
const obs$ = resource instanceof ODataEntitySetResource
? resource.fetch({ withCount, ...options })
: resource.fetch({
responseType: 'entities',
withCount,
...options,
});
return this._request(obs$, ({ entities, annots }) => {
this._annotations = annots;
return entities !== null
? this.assign(entities, {
reset: true,
add: add ?? true,
merge: merge ?? true,
remove: remove ?? true,
})
: [];
});
}
fetchAll({ add, merge, remove, withCount, ...options } = {}) {
const resource = this.resource();
if (!resource)
return throwError(() => new Error('fetchAll: Resource is null'));
const obs$ = resource.fetchAll({ withCount, ...options });
return this._request(obs$, ({ entities, annots }) => {
this._annotations = annots;
return entities !== null
? this.assign(entities, {
reset: true,
add: add ?? true,
merge: merge ?? true,
remove: remove ?? true,
})
: [];
});
}
fetchMany(top, { add, merge, remove, withCount, ...options } = {}) {
const resource = this.resource();
if (!resource)
return throwError(() => new Error('fetchMany: Resource is null'));
resource.query((q) => remove || this.length == 0 ? q.skip().clear() : q.skip(this.length));
const obs$ = resource.fetchMany(top, { withCount, ...options });
return this._request(obs$, ({ entities, annots }) => {
this._annotations = annots;
return entities !== null
? this.assign(entities, {
reset: true,
add: add ?? true,
merge: merge ?? true,
remove: remove ?? false,
})
: [];
});
}
fetchOne({ add, merge, remove, withCount, ...options } = {}) {
const resource = this.resource();
if (!resource)
return throwError(() => new Error('fetchOne: Resource is null'));
resource.query((q) => remove || this.length == 0 ? q.skip().clear() : q.skip(this.length));
const obs$ = resource.fetchOne({ withCount, ...options });
return this._request(obs$, ({ entity, annots }) => {
this._annotations = annots;
return entity !== null
? this.assign([entity], {
reset: true,
add: add ?? true,
merge: merge ?? true,
remove: remove ?? false,
})[0]
: null;
});
}
/**
* Save all models in the collection
* @param relModel The model is relationship
* @param method The method to use
* @param options HttpOptions
*/
save({ relModel = false, method, ...options } = {}) {
const resource = this.resource();
if (resource instanceof ODataPropertyResource)
return throwError(() => new Error('save: Resource is ODataPropertyResource'));
const toDestroyEntity = [];
const toRemoveReference = [];
const toDestroyContained = [];
const toCreateEntity = [];
const toAddReference = [];
const toCreateContained = [];
const toUpdateEntity = [];
const toUpdateContained = [];
this._entries.forEach(({ model, state }) => {
if (state === ODataModelState.Removed) {
if (relModel) {
toDestroyEntity.push(model);
}
else if (!model.isNew()) {
toRemoveReference.push(model);
}
else {
toDestroyContained.push(model);
}
}
else if (state === ODataModelState.Added) {
if (relModel) {
toCreateEntity.push(model);
}
else if (!model.isNew()) {
toAddReference.push(model);
}
else {
toCreateContained.push(model);
}
}
else if (model.hasChanged()) {
toUpdateEntity.push(model);
}
});
const obs$ = forkJoin([
...toDestroyEntity.map((m) => m.asEntity((e) => e.destroy(options))),
...toRemoveReference.map((m) => (this._model.meta.api.options.deleteRefBy === 'path'
? resource.key(m.key())
: resource)
.reference()
.remove(this._model.meta.api.options.deleteRefBy === 'id'
? m.asEntity((e) => e.resource())
: undefined, options)),
...toDestroyContained.map((m) => m.destroy(options)),
...toCreateEntity.map((m) => m.asEntity((e) => e.save({ method: 'create', ...options }))),
...toAddReference.map((m) => resource
.reference()
.add(m.asEntity((e) => e.resource()), options)),
...toCreateContained.map((m) => m.save({ method: 'create', ...options })),
...toUpdateEntity.map((m) => m.asEntity((e) => e.save({ method, ...options }))),
...toUpdateContained.map((m) => m.save({ method, ...options })),
]).pipe(defaultIfEmpty(null));
return this._request(obs$, () => {
this._entries = this._entries
.filter((entry) => entry.state !== ODataModelState.Removed)
.map((entry) => ({ ...entry, state: ODataModelState.Unchanged }));
return this.models();
});
}
_addServer(model, options) {
const resource = this.resource();
if (resource instanceof ODataNavigationPropertyResource) {
if (!model.isNew()) {
// Add Reference
return resource
.reference()
.add(model.asEntity((e) => e.resource()), options)
.pipe(map(() => model));
}
else {
// Create Contained
return resource.create(model.toEntity(), options).pipe(map(({ entity }) => {
if (entity) {
model.assign(entity);
}
return model;
}));
}
}
else if (resource instanceof ODataEntitySetResource) {
return model.asEntity((e) => e.save({ method: 'create', ...options }));
}
else {
return of(model);
}
}
_addModel(model, { silent = false, reset = false, reparent = false, merge = false, position, } = {}) {
let entry = this._findEntry(model);
if (entry !== undefined && entry.state !== ODataModelState.Removed) {
if (merge) {
entry.model.assign(model.toEntity(INCLUDE_DEEP));
}
return entry.model;
}
if (entry !== undefined && entry.state === ODataModelState.Removed) {
const index = this._entries.indexOf(entry);
this._entries.splice(index, 1);
}
// Create Entry
entry = {
state: reset ? ODataModelState.Unchanged : ODataModelState.Added,
model,
key: model.key(),
};
// Set Parent
if (reparent)
model._parent = [this, null];
// Subscribe
this._link(entry);
// If position is undefined and the collection is sorted, find the right position
if (position === undefined && this._sortBy !== null) {
for (let index = 0; index < this._entries.length; index++) {
if (this._compare(model, this._entries[index], this._sortBy, 0) < 0) {
position = index;
break;
}
}
}
// Now add
if (position !== undefined)
this._entries.splice(position, 0, entry);
else
this._entries.push(entry);
if (!silent) {
model.events$.trigger(ODataModelEventType.Add, {
collection: this,
options: { index: position },
});
}
return entry.model;
}
add(model, { silent = false, reparent = false, server = true, merge = false, position, reset, } = {}) {
const _addModel = (m, reset) => this._addModel(m, { silent, position, merge, reparent, reset });
return server
? this._request(this._addServer(model), (model) => _addModel(model, reset ?? true))
: of(_addModel(model, reset ?? false));
}
_removeServer(model, options) {
let resource = this.resource();
if (resource instanceof ODataNavigationPropertyResource) {
if (!model.isNew()) {
// Remove Reference
const target = this._model.meta.api.options.deleteRefBy === 'id'
? model.asEntity((e) => e.resource())
: undefined;
if (this._model.meta.api.options.deleteRefBy === 'path') {
resource = resource.key(model.key());
}
return resource
.reference()
.remove(target, options)
.pipe(map(() => model));
}
else {
// Remove Contained
return resource.destroy(options).pipe(map(() => model));
}
}
else if (resource instanceof ODataEntitySetResource) {
return model.asEntity((e) => e.destroy(options));
}
else {
return of(model);
}
}
_removeModel(model, { silent = false, reset = false, } = {}) {
const entry = this._findEntry(model);
if (entry === undefined || entry.state === ODataModelState.Removed) {
return model;
}
// Now remove
const index = this._entries.indexOf(entry);
this._entries.splice(index, 1);
if (!(reset || entry.state === ODataModelState.Added)) {
// Move to end of array and mark as removed
entry.state = ODataModelState.Removed;
this._entries.push(entry);
}
// Trigger Event
if (!silent) {
model.events$.trigger(ODataModelEventType.Remove, {
collection: this,
options: { index: index },
});
}
this._unlink(entry);
return entry.model;
}
remove(model, { silent = false, server = true, reset, } = {}) {
const _removeModel = (m, reset) => this._removeModel(m, { silent, reset });
return server
? this._request(this._removeServer(model), (model) => _removeModel(model, reset ?? true))
: of(_removeModel(model, reset ?? false));
}
_moveModel(model, position) {
const entry = this._findEntry(model);
if (entry === undefined || entry.state === ODataModelState.Removed) {
return model;
}
// Now remove
const index = this._entries.indexOf(entry);
this._entries.splice(index, 1);
this._entries.splice(position, 0, entry);
return entry.model;
}
create(attrs = {}, { silent = false, server = true, } = {}) {
const model = this.modelFactory(attrs);
return (model.isValid() && server ? model.save() : of(model)).pipe(switchMap((model) => this.add(model, { silent, server })), map(() => model));
}
set(path, value, {}) {
const pathArray = (Types.isArray(path) ? path : path.match(/([^[.\]])+/g));
if (pathArray.length === 0)
return undefined;
if (pathArray.length > 1) {
const model = this._entries[Number(pathArray[0])].model;
return model.set(pathArray.slice(1), value, {});
}
if (pathArray.length === 1 && ODataModelOptions.isModel(value)) {
const models = this.models();
const index = Number(pathArray[0]);
models[index] = value;
this.assign(models, { reparent: true });
return value;
}
}
get(path) {
const pathArray = (Types.isArray(path) ? path : `${path}`.match(/([^[.\]])+/g));
if (pathArray.length === 0)
return undefined;
const value = this.models()[Number(pathArray[0])];
if (pathArray.length > 1 && ODataModelOptions.isModel(value)) {
return value.get(pathArray.slice(1));
}
return value;
}
has(path) {
const pathArray = (Types.isArray(path) ? path : `${path}`.match(/([^[.\]])+/g));
if (pathArray.length === 0)
return false;
const value = this.models()[Number(pathArray[0])];
if (pathArray.length > 1 && ODataModelOptions.isModel(value)) {
return value.has(pathArray.slice(1));
}
return value !== undefined;
}
reset({ path, silent = false, } = {}) {
let toAdd = [];
let toChange = [];
let toRemove = [];
if (path !== undefined) {
// Reset by path
const pathArray = (Types.isArray(path) ? path : `${path}`.match(/([^[.\]])+/g));
const index = Number(pathArray[0]);
if (!Number.isNaN(index)) {
const model = this.models()[index];
if (ODataModelOptions.isModel(model)) {
const entry = this._findEntry(model);
if (entry.state === ODataModelState.Unchanged &&
entry.model.hasChanged()) {
toChange = [entry];
}
path = pathArray.slice(1);
}
}
}
else {
// Reset all
toAdd = this._entries.filter((e) => e.state === ODataModelState.Removed);
toChange = this._entries.filter((e) => e.state === ODataModelState.Unchanged && e.model.hasChanged());
toRemove = this._entries.filter((e) => e.state === ODataModelState.Added);
}
toRemove.forEach((entry) => {
this._removeModel(entry.model, { silent });
});
toAdd.forEach((entry) => {
this._addModel(entry.model, { silent });
});
toChange.forEach((entry) => {
entry.model.reset({ path, silent });
entry.state = ODataModelState.Unchanged;
});
if (!silent &&
(toAdd.length > 0 || toRemove.length > 0 || toChange.length > 0)) {
this.events$.trigger(ODataModelEventType.Reset, {
options: {
added: toAdd.map((e) => e.model),
removed: toRemove.map((e) => e.model),
changed: toChange.map((e) => e.model),
},
});
}
}
clear({ silent = false } = {}) {
const toRemove = this.models();
toRemove.forEach((m) => {
this._removeModel(m, { silent });
});
this._entries = [];
if (!silent) {
this.events$.trigger(ODataModelEventType.Update, {
options: { removed: toRemove },
});
}
}
assign(objects, { add = true, merge = true, remove = true, reset = false, reparent = false, silent = false, } = {}) {
const offset = remove ? 0 : this.length;
const models = [];
const toAdd = [];
const toMerge = [];
const toRemove = [];
objects.forEach((obj, index) => {
const model = ODataModelOptions.isModel(obj)
? obj
: this.modelFactory(obj, {
reset,
});
const position = index + offset;
// Try find entry
const entry = this._findEntry(model);
if (merge && entry !== undefined) {
if (entry.model !== model) {
entry.model.assign(model.toEntity({
client_id: true,
...INCLUDE_DEEP,
}), { add, merge, remove, reset, silent });
// Model Change?
if (entry.model.hasChanged())
toMerge.push(entry.model);
}
if (reset)
entry.state = ODataModelState.Unchanged;
if (!models.includes(entry.model)) {
models.push(entry.model);
}
}
else if (add) {
// Add
toAdd.push(model);
this._addModel(model, { silent, reset, reparent, position });
models.push(model);
}
});
if (remove) {
[...this._entries].forEach((entry) => {
const model = entry.model;
if (!models.includes(model)) {
this._removeModel(model, { silent, reset });
toRemove.push(model);
}
});
}
if (this.models()
.slice(offset)
.some((m, i) => m !== models[i])) {
models.forEach((m, i) => this._moveModel(m, i));
this.events$.trigger(ODataModelEventType.Sort);
}
if (!silent &&
(toAdd.length > 0 || toRemove.length > 0 || toMerge.length > 0)) {
this.events$.trigger(reset ? ODataModelEventType.Reset : ODataModelEventType.Update, {
options: {
added: toAdd,
removed: toRemove,
merged: toMerge,
},
});
}
return models;
}
query(ctx) {
const resource = this.resource();
if (resource) {
resource.query(ctx);
this.attach(resource);
}
return this;
}
callFunction(name, params, responseType, options = {}) {
const resource = this.resource();
if (!(resource instanceof ODataEntitySetResource))
return throwError(() => new Error("callFunction: Can't call function without ODataEntitySetResource"));
const func = resource.function(name).query((q) => q.restore(options));
switch (responseType) {
case 'property':
return this._request(func.callProperty(params, options), (resp) => resp);
case 'model':
return this._request(func.callModel(params, options), (resp) => resp);
case 'collection':
return this._request(func.callCollection(params, options), (resp) => resp);
default:
return this._request(func.call(params, { responseType, ...options }), (resp) => resp);
}
}
callAction(name, params, responseType, options = {}) {
const resource = this.resource();
if (!(resource instanceof ODataEntitySetResource)) {
return throwError(() => new Error(`callAction: Can't call action without ODataEntitySetResource`));
}
const action = resource.action(name).query((q) => q.restore(options));
switch (responseType) {
case 'property':
return this._request(action.callProperty(params, options), (resp) => resp);
case 'model':
return this._request(action.callModel(params, options), (resp) => resp);
case 'collection':
return this._request(action.callCollection(params, options), (resp) => resp);
default:
return this._request(action.call(params, { responseType, ...options }), (resp) => resp);
}
}
_unlink(entry) {
if (entry.subscription) {
entry.subscription.unsubscribe();
entry.subscription = undefined;
}
}
_link(entry) {
if (entry.subscription) {
throw new Error('Collection: Subscription already exists');
}
entry.subscription = entry.model.events$.subscribe((event) => {
if (event.canContinueWith(this)) {
if (event.model === entry.model) {
if (event.type === ODataModelEventType.Destroy) {
this._removeModel(entry.model, { reset: true });
}
else if (event.type === ODataModelEventType.Change &&
event.options?.key) {
entry.key = entry.model.key();
}
}
const index = event.options?.index ?? this.models().indexOf(entry.model);
this.events$.emit(event.push(this, index));
}
});
}
_findEntry(model) {
return this._entries.find((entry) => (entry.key !== undefined &&
model.key() !== undefined &&
Types.isEqual(entry.key, model.key())) ||
entry.model.equals(model));
}
// Collection functions
equals(other) {
return this === other;
}
get [Symbol.toStringTag]() {
return 'Collection';
}
[Symbol.iterator]() {
let pointer = 0;
const models = this.models();
return {
next() {
return {
done: pointer === models.length,
value: models[pointer++],
};
},
};
}
filter(predicate, thisArg) {
return this.models().filter(predicate, thisArg);
}
map(callbackfn, thisArg) {
return this.models().map(callbackfn, thisArg);
}
find(predicate) {
return this.models().find(predicate);
}
reduce(callbackfn, initialValue) {
return this.models().reduce(callbackfn, initialValue);
}
first() {
return this.models()[0];
}
last() {
const models = this.models();
return models[models.length - 1];
}
next(model) {
const index = this.indexOf(model);
if (index === -1 || index === this.length - 1) {
return undefined;
}
return this.get(index + 1);
}
prev(model) {
const index = this.indexOf(model);
if (index <= 0) {
return undefined;
}
return this.get(index - 1);
}
every(predicate) {
return this.models().every(predicate);
}
some(predicate) {
return this.models().some(predicate);
}
includes(model, start = 0) {
return this.some((m, i) => i >= start && m.equals(model));
}
indexOf(model) {
const models = this.models();
const m = models.find((m) => m.equals(model));
return !m ? -1 : models.indexOf(m);
}
forEach(predicate, thisArg) {
return this.models().forEach(predicate, thisArg);
}
isEmpty() {
// Local length == 0 and if exist remote count is 0 or undefined
return this.length === 0 && !this.annots().count;
}
//#region Sort
_compare(e1, e2, by, index) {
const m1 = ODataModelOptions.isModel(e1)
? e1
: e1.model;
const m2 = ODataModelOptions.isModel(e2)
? e2
: e2.model;
const value1 = m1.get(by[index].field);
const value2 = m2.get(by[index].field);
let result = 0;
if (value1 == null && value2 != null)
result = -1;
else if (value1 != null && value2 == null)
result = 1;
else if (value1 == null && value2 == null)
result = 0;
else if ((typeof value1 == 'string' || value1 instanceof String) && value1.localeCompare && value1 != value2)
result = value1.localeCompare(value2);
else if (value1 == value2)
return by.length - 1 > index ? this._compare(e1, e2, by, index + 1) : 0;
else if (by[index].comparator !== undefined)
result = by[index].comparator(value1, value2);
else {
result = value1 < value2 ? -1 : 1;
}
return (by[index].order ?? 1) * result;
}
_sortBy = null;
isSorted() {
return this._sortBy !== null;
}
sort(by, { silent } = {}) {
this._sortBy = by;
this._entries = this._entries.sort((e1, e2) => this._compare(e1, e2, by, 0));
if (!silent) {
this.events$.trigger(ODataModelEventType.Sort);
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29sbGVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2FuZ3VsYXItb2RhdGEvc3JjL2xpYi9tb2RlbHMvY29sbGVjdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsVUFBVSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVELE9BQU8sRUFBRSxjQUFjLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMxRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDeEMsT0FBTyxFQUdMLHNCQUFzQixFQUV0QiwrQkFBK0IsRUFFL0IscUJBQXFCLEdBR3RCLE1BQU0sY0FBYyxDQUFDO0FBRXRCLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV2QyxPQUFPLEVBQ0wsWUFBWSxFQUlaLHNCQUFzQixFQUN0QixtQkFBbUIsRUFFbkIsaUJBQWlCLEVBQ2pCLGVBQWUsR0FDaEIsTUFBTSxXQUFXLENBQUM7QUFDbkIsT0FBTyxFQUNMLHdCQUF3QixFQUN4QixzQkFBc0IsR0FDdkIsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixNQUFNLE9BQU8sZUFBZTtJQUcxQixNQUFNLENBQUMsS0FBSyxHQUE2QixJQUFJLENBQUM7SUFDOUMsT0FBTyxHQUtJLElBQUksQ0FBQztJQUNoQixTQUFTLEdBSUUsSUFBSSxDQUFDO0lBQ2hCLFVBQVUsR0FZSixFQUFFLENBQUM7SUFDVCxZQUFZLENBQStCO0lBQzNDLFFBQVEsR0FBNEIsRUFBRSxDQUFDO0lBQ3ZDLE1BQU0sQ0FBb0I7SUFFMUIsTUFBTTtRQUNKLE9BQU8sSUFBSSxDQUFDLFFBQVE7YUFDakIsTUFBTSxDQUFDLENBQUMsQ0FB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