relution-sdk
Version:
Relution Software Development Kit for TypeScript and JavaScript
457 lines • 61.2 kB
JavaScript
/*
* @file livedata/SyncContext.ts
* Relution SDK
*
* Created by Thomas Beckmann on 26.06.2015
* Copyright 2016 M-Way Solutions GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @module livedata
*/
/** */
"use strict";
var _ = require('lodash');
var diag = require('../core/diag');
var GetQuery_1 = require('../query/GetQuery');
var JsonFilterVisitor_1 = require('../query/JsonFilterVisitor');
var SortOrderComparator_1 = require('../query/SortOrderComparator');
/**
* receives change messages and updates collections.
*/
var SyncContext = (function () {
/**
* captures option values forming a GetQuery.
*
* @param options to merge.
* @constructor
*/
function SyncContext() {
var _this = this;
var options = [];
for (var _i = 0; _i < arguments.length; _i++) {
options[_i - 0] = arguments[_i];
}
/**
* relevant parameters for paging, filtering and sorting.
*
* @type {Relution.livedata.GetQuery}
*/
this.getQuery = new GetQuery_1.GetQuery();
// merge options forming a GetQuery
options.forEach(function (json) {
if (json) {
_this.getQuery.merge(new GetQuery_1.GetQuery().fromJSON(json));
}
});
this.getQuery.optimize();
// compute local members
this.pageSize = this.getQuery.limit;
this.compareFn = this.getQuery.sortOrder && SortOrderComparator_1.jsonCompare(this.getQuery.sortOrder);
this.filterFn = this.getQuery.filter && JsonFilterVisitor_1.jsonFilter(this.getQuery.filter);
}
/**
* reads an additional page of data into the collection.
*
* When async processing is done, a more attribute is set on the options object in case additional data might be
* available which can be loaded by calling this method again. Likewise an end attribute is set if the data is
* fully loaded.
*
* @param {object} collection to load data into.
* @param {object} options such as pageSize to retrieve.
* @return {Promise} promise of the load operation.
*
* @see Collection#fetchMore()
*/
SyncContext.prototype.fetchMore = function (collection, options) {
if (options === void 0) { options = {}; }
var getQuery = this.getQuery;
options = _.defaults(options, {
limit: options.pageSize || this.pageSize || getQuery.limit,
sortOrder: getQuery.sortOrder,
filter: getQuery.filter,
fields: getQuery.fields
});
// prepare a query for the next page of data to load
options.offset = (getQuery.offset | 0) + collection.models.length;
// this must be set in options to state we handle it
options.syncContext = this;
// setup callbacks handling processing of results, do not use promises as these execute too late...
// Notice, since we call collection.sync() directly, the signature of success/error callbacks here is ajax-style.
// However, the user-provided callbacks are to being called backbone.js-style with collection and object.
var oldSuccess = options.success;
var oldError = options.error;
options.success = function fetchMoreSuccess(models) {
// restore callbacks
options.success = oldSuccess;
options.error = oldError;
// update models
if (models) {
// add models to collection, if any
if (models.length <= 0) {
// reached the end
delete options.more;
}
else {
// read additional data
if (options.syncContext.compareFn) {
// notice, existing range of models is sorted by definition already
options.at = options.syncContext.insertionPoint(models[0], collection.models);
}
models = collection.add(models, options) || models;
// adjust query parameter
getQuery.limit = collection.models.length;
if (options.syncContext.getQuery.limit > getQuery.limit) {
// reached the end
delete options.more;
}
else {
// more data to load
options.more = true;
delete options.end;
}
}
// reached the end?
if (!options.more) {
getQuery.limit = undefined; // open end
options.end = true;
}
}
// restore query parameter
options.syncContext.getQuery = getQuery;
// call user success callback
if (options.success) {
models = options.success.call(this, collection, models, options) || models;
}
return models;
};
options.error = function fetchMoreError(error) {
// restore callbacks
options.success = oldSuccess;
options.error = oldError;
// restore query parameter
options.syncContext.getQuery = getQuery;
// call user error callback
if (options.error) {
error = options.error.call(this, collection, error, options) || error;
}
return error;
};
// fire up the page load
this.getQuery = new GetQuery_1.GetQuery(getQuery);
this.getQuery.limit = getQuery.limit + options.limit;
return collection.sync(options.method || 'read', collection, options);
};
/**
* reads a page of data into the collection.
*
* When async processing is done, a next/prev attribute is set on the options object in case additional pages might
* be available which can be loaded by calling this method again.
*
* @param {object} collection to load data into.
* @param {object} options incl. offset and limit of page to retrieve.
* @return {Promise} promise of the load operation.
*/
SyncContext.prototype.fetchRange = function (collection, options) {
if (options === void 0) { options = {}; }
// this must be set in options to state we handle it
options.syncContext = this;
// prepare a query for the page of data to load
var oldQuery = this.getQuery;
var newQuery = new GetQuery_1.GetQuery(oldQuery);
if (options.offset >= 0) {
newQuery.offset = options.offset;
}
else if (options.offset < 0) {
newQuery.offset = undefined;
}
var oldLimit = options.limit;
if (options.limit > 0) {
newQuery.limit = options.limit + 1;
options.limit = newQuery.limit;
}
else if (options.limit <= 0) {
newQuery.limit = undefined;
}
// setup callbacks handling processing of results, do not use promises as these execute too late...
// Notice, since we call collection.sync() directly, the signature of success/error callbacks here is ajax-style.
// However, the user-provided callbacks are to being called backbone.js-style with collection and object.
var oldSuccess = options.success;
var oldError = options.error;
options.success = function fetchRangeSuccess(models) {
// restore callbacks and limit
options.success = oldSuccess;
options.error = oldError;
if (oldLimit !== undefined) {
options.limit = oldLimit;
}
// update models
if (models) {
// add models to collection, if any
if (models.length > 0) {
// adjust query parameter
options.next = newQuery.limit && models.length >= newQuery.limit;
if (options.next) {
// trick here was to read one more item to see if there is more to come
models.length = models.length - 1;
}
// realize the page
models = collection.reset(models, options) || models;
}
else {
// reached the end
delete options.next;
}
options.prev = newQuery.offset > 0;
}
// call user success callback
if (options.success) {
models = options.success.call(this, collection, models, options) || models;
}
return models;
};
options.error = function fetchMoreError(error) {
// restore callbacks and limit
options.success = oldSuccess;
options.error = oldError;
if (oldLimit !== undefined) {
options.limit = oldLimit;
}
// restore query parameter
options.syncContext.getQuery = oldQuery;
// call user error callback
if (options.error) {
error = options.error.call(this, collection, error, options) || error;
}
return error;
};
// fire up the page load
this.getQuery = newQuery;
return collection.sync(options.method || 'read', collection, options);
};
/**
* reads the next page of data into the collection.
*
* @param {object} options such as pageSize to retrieve.
* @return {Promise} promise of the load operation.
*
* @see Collection#fetchNext()
*/
SyncContext.prototype.fetchNext = function (collection, options) {
if (options === void 0) { options = {}; }
options.limit = options.pageSize || this.pageSize || this.getQuery.limit;
options.offset = (this.getQuery.offset | 0) + collection.models.length;
return this.fetchRange(collection, options);
};
/**
* reads the previous page of data into the collection.
*
* @param {object} options such as pageSize to retrieve.
* @return {Promise} promise of the load operation.
*
* @see Collection#fetchPrev()
*/
SyncContext.prototype.fetchPrev = function (collection, options) {
if (options === void 0) { options = {}; }
options.limit = options.pageSize || this.pageSize || this.getQuery.limit;
options.offset = (this.getQuery.offset | 0) - options.limit;
return this.fetchRange(collection, options);
};
SyncContext.prototype.filterAttributes = function (attrs, options) {
return this.filterFn ? attrs.filter(this.filterFn) : attrs;
};
SyncContext.prototype.sortAttributes = function (attrs, options) {
return this.compareFn ? attrs.sort(this.compareFn) : attrs;
};
SyncContext.prototype.rangeAttributes = function (attrs, options) {
var offset = options && options.offset || this.getQuery.offset;
if (offset > 0) {
attrs.splice(0, offset);
}
var limit = options && options.limit || this.getQuery.limit;
if (limit < attrs.length) {
attrs.length = limit;
}
return attrs;
};
SyncContext.prototype.processAttributes = function (attrs, options) {
attrs = this.filterAttributes(attrs, options);
attrs = this.sortAttributes(attrs, options);
attrs = this.rangeAttributes(attrs, options);
return attrs;
};
/**
* receives change messages.
*
* Change messages are communicated by the SyncStore indirectly triggering a sync:channel event. This happens
* regardless of whether the change originates local or remote. The context then alters the backbone data
* incorporating the change.
*
* @param store
* @param collection
* @param msg
*/
SyncContext.prototype.onMessage = function (store, collection, msg) {
var options = {
collection: collection,
entity: collection.entity,
merge: msg.method === 'patch',
parse: true,
fromMessage: true
};
var newId = collection.modelId(msg.data); // modelId(attrs) missing in DefinitelyTyped definitions
var oldId = msg.id || newId;
if (oldId === 'all') {
collection.reset(msg.data || {}, options);
return;
}
// update the collection
var model = oldId && collection.get(oldId);
switch (msg.method) {
case 'create':
/* falls through */
case 'update':
if (newId !== oldId) {
diag.debug.warn('updating id ' + oldId + ' to ' + newId);
}
if (!model) {
// create model in case it does not exist
model = new options.collection.model(msg.data, options);
if (this.filterFn && !this.filterFn(model.attributes)) {
break; // filtered
}
if (model.validationError) {
collection.trigger('invalid', this, model.validationError, options);
}
else {
var index = collection.models.length;
if (this.compareFn && index > 0) {
options.at = index = this.insertionPoint(model.attributes, collection.models);
}
// look at index and respect offset/limit eventually ignoring model or removing some,
// the not operators below cause proper handling when offset or limit is undefined...
/* jshint -W018 */
if ((!(this.getQuery.offset > 0) || index > 0) && !(index >= this.getQuery.limit)) {
/* jshint +W018 */
collection.add(model, options);
if (this.getQuery.limit && collection.models.length > this.getQuery.limit) {
collection.remove(collection.models[collection.models.length - 1], options);
}
}
}
break;
}
/* falls through */
case 'patch':
if (model) {
// update model unless it is filtered
model.set(msg.data, options);
if (this.filterFn && !this.filterFn(model.attributes)) {
// eventually the model is filtered
collection.remove(model, options);
}
else if (this.compareFn) {
// eventually the model changes position in collection.models
var oldIndex = collection.models.indexOf(model);
this.lastInsertionPoint = oldIndex >= 0 ? oldIndex : undefined;
var newIndex = this.insertionPoint(model.attributes, collection.models);
if (oldIndex !== newIndex) {
// following acts just like backbone.Collection.sort()
collection.models.splice(oldIndex, 1);
collection.models.splice(newIndex, 0, model);
collection.trigger('sort', collection, options);
}
}
}
break;
case 'delete':
if (model) {
// remove model
collection.remove(model, options);
}
break;
}
};
/**
* computes the insertion point of attributes into models sorted by compareFn.
*
* This is used to compute the at-index of backbone.js add() method options when adding models to a sorted collection.
*
* @param attributes being inserted.
* @param models sorted by compareFn.
* @return {number} insertion point.
*/
SyncContext.prototype.insertionPoint = function (attributes, models) {
if (this.lastInsertionPoint !== undefined) {
// following performs two comparisons at the last insertion point to take advantage of locality,
// this means we don't subdivide evenly but check tiny interval at insertion position firstly...
var start = Math.max(0, this.lastInsertionPoint);
var end = Math.min(models.length, this.lastInsertionPoint + 3);
if (end - start > 1) {
// focus on (start;end] range speeding up binary searches by taking locality into account
var point = this.insertionPointBinarySearch(attributes, models, start, end);
if (point >= end) {
// select upper interval
if (point < models.length) {
point = this.insertionPointBinarySearch(attributes, models, point, models.length);
}
}
else if (point < start) {
// select lower interval
if (point > 0) {
point = this.insertionPointBinarySearch(attributes, models, 0, point);
}
}
this.lastInsertionPoint = point;
return point;
}
}
// locality not applicable or did not work
this.lastInsertionPoint = this.insertionPointBinarySearch(attributes, models, 0, models.length);
return this.lastInsertionPoint;
};
/**
* performs a binary search for insertion point of attributes into models[start:end] sorted by compareFn.
*
* @param attributes being inserted.
* @param models sorted by compareFn.
* @param compare function as of Array.sort().
* @param start inclusive index of search interval.
* @param end exclusive index of search interval.
* @return {number} insertion point.
*/
SyncContext.prototype.insertionPointBinarySearch = function (attributes, models, start, end) {
var pivot = (start + end) >> 1;
var delta = this.compareFn(attributes, models[pivot].attributes);
if (end - start <= 1) {
return delta < 0 ? pivot : pivot + 1;
}
else if (delta < 0) {
// select lower half
return this.insertionPointBinarySearch(attributes, models, start, pivot);
}
else if (delta > 0) {
// select upper half
if (++pivot < end) {
return this.insertionPointBinarySearch(attributes, models, pivot, end);
}
}
else {
}
return pivot;
};
return SyncContext;
}());
exports.SyncContext = SyncContext;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3luY0NvbnRleHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbGl2ZWRhdGEvU3luY0NvbnRleHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNIOztHQUVHO0FBQ0gsTUFBTTs7QUFFTixJQUFZLENBQUMsV0FBTSxRQUFRLENBQUMsQ0FBQTtBQUU1QixJQUFZLElBQUksV0FBTSxjQUFjLENBQUMsQ0FBQTtBQUVyQyx5QkFBdUIsbUJBQW1CLENBQUMsQ0FBQTtBQUMzQyxrQ0FBdUMsNEJBQTRCLENBQUMsQ0FBQTtBQUNwRSxvQ0FBeUMsOEJBQThCLENBQUMsQ0FBQTtBQU14RTs7R0FFRztBQUNIO0lBNkJFOzs7OztPQUtHO0lBQ0g7UUFuQ0YsaUJBd2NDO1FBcmFvQixpQkFBZ0I7YUFBaEIsV0FBZ0IsQ0FBaEIsc0JBQWdCLENBQWhCLElBQWdCO1lBQWhCLGdDQUFnQjs7UUFqQ25DOzs7O1dBSUc7UUFDSyxhQUFRLEdBQWEsSUFBSSxtQkFBUSxFQUFFLENBQUM7UUE2QjFDLG1DQUFtQztRQUNuQyxPQUFPLENBQUMsT0FBTyxDQUFDLFVBQUMsSUFBSTtZQUNuQixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2dCQUNULEtBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksbUJBQVEsRUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3JELENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7UUFFekIsd0JBQXdCO1FBQ3hCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUM7UUFDcEMsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsSUFBSSxpQ0FBVyxDQUFDLElBQUksQ0FBQyxRQUFR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Q0FBQyxTQUFTLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNqRSxFQUFFLENBQUMsQ0FBQyxHQUFHLEdBQUcsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDckIsTUFBTSxDQUFDLEtBQUssR0FBRyxDQUFDLEdBQUcsS0FBSyxHQUFHLEtBQUssR0FBRyxDQUFDLENBQUM7UUFDdkMsQ0FBQztRQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNyQixvQkFBb0I7WUFDcEIsTUFBTSxDQUFDLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMzRSxDQUFDO1FBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3JCLG9CQUFvQjtZQUNwQixFQUFFLENBQUMsQ0FBQyxFQUFFLEtBQUssR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNsQixNQUFNLENBQUMsSUFBSSxDQUFDLDBCQUEwQixDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3pFLENBQUM7UUFDSCxDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7UUFFUixDQUFDO1FBQ0QsTUFBTSxDQUFDLEtBQUssQ0FBQztJQUNmLENBQUM7SUFDSCxrQkFBQztBQUFELENBQUMsQUF4Y0QsSUF3Y0M7QUF4Y1ksbUJBQVcsY0F3Y3ZCLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQGZpbGUgbGl2ZWRhdGEvU3luY0NvbnRleHQudHNcbiAqIFJlbHV0aW9uIFNES1xuICpcbiAqIENyZWF0ZWQgYnkgVGhvbWFzIEJlY2ttYW5uIG9uIDI2LjA2LjIwMTVcbiAqIENvcHlyaWdodCAyMDE2IE0tV2F5IFNvbHV0aW9ucyBHbWJIXG4gKlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cbi8qKlxuICogQG1vZHVsZSBsaXZlZGF0YVxuICovXG4vKiogKi9cblxuaW1wb3J0ICogYXMgXyBmcm9tICdsb2Rhc2gnO1xuXG5pbXBvcnQgKiBhcyBkaWFnIGZyb20gJy4uL2NvcmUvZGlhZyc7XG5cbmltcG9ydCB7R2V0UXVlcnl9IGZyb20gJy4uL3F1ZXJ5L0dldFF1ZXJ5JztcbmltcG9ydCB7anNvbkZpbHRlciwgSnNvbkZpbHRlckZufSBmcm9tICcuLi9xdWVyeS9Kc29uRmlsdGVyVmlzaXRvcic7XG5pbXBvcnQge2pzb25Db21wYXJlLCBKc29uQ29tcGFyZUZufSBmcm9tICcuLi9xdWVyeS9Tb3J0T3JkZXJDb21wYXJhdG9yJztcbmltcG9ydCB7U3RvcmV9IGZyb20gJy4vU3RvcmUnO1xuaW1wb3J0IHtNb2RlbH0gZnJvbSAnLi9Nb2RlbCc7XG5pbXBvcnQge0NvbGxlY3Rpb259IGZyb20gJy4vQ29sbGVjdGlvbic7XG5pbXBvcnQge0xpdmVEYXRhTWVzc2FnZX0gZnJvbSAnLi9MaXZlRGF0YU1lc3NhZ2UnO1xuXG4vKipcbiAqIHJlY2VpdmVzIGNoYW5nZSBtZXNzYWdlcyBhbmQgdXBkYXRlcyBjb2xsZWN0aW9ucy5cbiAqL1xuZXhwb3J0IGNsYXNzIFN5bmNDb250ZXh0IHtcblxuICAvKipcbiAgICogcmVsZXZhbnQgcGFyYW1ldGVycyBmb3IgcGFnaW5nLCBmaWx0ZXJpbmcgYW5kIHNvcnRpbmcuXG4gICAqXG4gICAqIEB0eXBlIHtSZWx1dGlvbi5saXZlZGF0YS5HZXRRdWVyeX1cbiAgICovXG4gIHByaXZhdGUgZ2V0UXVlcnk6IEdldFF1ZXJ5ID0gbmV3IEdldFF1ZXJ5KCk7XG5cbiAgLyoqXG4gICAqIGxpbWl0IG9mIGdldFF1ZXJ5IGNhcHR1cmVkIGF0IGNvbnN0cnVjdGlvbiB0aW1lLlxuICAgKi9cbiAgcHJpdmF0ZSBwYWdlU2l6ZTogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiB1c2VkIHRvIHNwZWVkIHVwIGluc2VydGlvbiBwb2ludCB3aGVuIGRvaW5nIGNvbnNlY3V0aXZlIGluc2VydGlvbnMgaW50byBzb3J0ZWQgcmFuZ2VzLlxuICAgKi9cbiAgcHJpdmF0ZSBsYXN0SW5zZXJ0aW9uUG9pbnQ6IG51bWJlcjtcblxuICAvKipcbiAgICogd2hlbiBzZXQsIGRlZmluZXMgc29ydGluZyBvZiBjb2xsZWN0aW9uLlxuICAgKi9cbiAgcHJpdmF0ZSBjb21wYXJlRm46IEpzb25Db21wYXJlRm48YW55PjtcblxuICAvKipcbiAgICogd2hlbiBzZXQsIGRlZmluZXMgZmlsdGVyaW5nIG9mIGNvbGxlY3Rpb24uXG4gICAqL1xuICBwcml2YXRlIGZpbHRlckZuOiBKc29uRmlsdGVyRm48YW55PjtcblxuICAvKipcbiAgICogY2FwdHVyZXMgb3B0aW9uIHZhbHVlcyBmb3JtaW5nIGEgR2V0UXVlcnkuXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zIHRvIG1lcmdlLlxuICAgKiBAY29uc3RydWN0b3JcbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3RvciguLi5vcHRpb25zOiB7fVtdKSB7XG4gICAgLy8gbWVyZ2Ugb3B0aW9ucyBmb3JtaW5nIGEgR2V0UXVlcnlcbiAgICBvcHRpb25zLmZvckVhY2goKGpzb24pID0+IHtcbiAgICAgIGlmIChqc29uKSB7XG4gICAgICAgIHRoaXMuZ2V0UXVlcnkubWVyZ2UobmV3IEdldFF1ZXJ5KCkuZnJvbUpTT04oanNvbikpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuZ2V0UXVlcnkub3B0aW1pemUoKTtcblxuICAgIC8vIGNvbXB1dGUgbG9jYWwgbWVtYmVyc1xuICAgIHRoaXMucGFnZVNpemUgPSB0aGlzLmdldFF1ZXJ5LmxpbWl0O1xuICAgIHRoaXMuY29tcGFyZUZuID0gdGhpcy5nZXRRdWVyeS5zb3J0T3JkZXIgJiYganNvbkNvbXBhcmUodGhpcy5nZXRRdWVyeS5zb3J0T3JkZXIpO1xuICAgIHRoaXMuZmlsdGVyRm4gPSB0aGlzLmdldFF1ZXJ5LmZpbHRlciAmJiBqc29uRmlsdGVyKHRoaXMuZ2V0UXVlcnkuZmlsdGVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiByZWFkcyBhbiBhZGRpdGlvbmFsIHBhZ2Ugb2YgZGF0YSBpbnRvIHRoZSBjb2xsZWN0aW9uLlxuICAgKlxuICAgKiBXaGVuIGFzeW5jIHByb2Nlc3NpbmcgaXMgZG9uZSwgYSBtb3JlIGF0dHJpYnV0ZSBpcyBzZXQgb24gdGhlIG9wdGlvbnMgb2JqZWN0IGluIGNhc2UgYWRkaXRpb25hbCBkYXRhIG1pZ2h0IGJlXG4gICAqIGF2YWlsYWJsZSB3aGljaCBjYW4gYmUgbG9hZGVkIGJ5IGNhbGxpbmcgdGhpcyBtZXRob2QgYWdhaW4uIExpa2V3aXNlIGFuIGVuZCBhdHRyaWJ1dGUgaXMgc2V0IGlmIHRoZSBkYXRhIGlzXG4gICAqIGZ1bGx5IGxvYWRlZC5cbiAgICpcbiAgICogQHBhcmFtIHtvYmplY3R9IGNvbGxlY3Rpb24gdG8gbG9hZCBkYXRhIGludG8uXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIHN1Y2ggYXMgcGFnZVNpemUgdG8gcmV0cmlldmUuXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugb2YgdGhlIGxvYWQgb3BlcmF0aW9uLlxuICAgKlxuICAgKiBAc2VlIENvbGxlY3Rpb24jZmV0Y2hNb3JlKClcbiAgICovXG4gIHB1YmxpYyBmZXRjaE1vcmUoY29sbGVjdGlvbjogQ29sbGVjdGlvbiwgb3B0aW9uczogYW55ID0ge30pIHtcbiAgICB2YXIgZ2V0UXVlcnkgPSB0aGlzLmdldFF1ZXJ5O1xuICAgIG9wdGlvbnMgPSBfLmRlZmF1bHRzKG9wdGlvbnMsIHtcbiAgICAgIGxpbWl0OiBvcHRpb25zLnBhZ2VTaXplIHx8IHRoaXMucGFnZVNpemUgfHwgZ2V0UXVlcnkubGltaXQsXG4gICAgICBzb3J0T3JkZXI6IGdldFF1ZXJ5LnNvcnRPcmRlcixcbiAgICAgIGZpbHRlcjogZ2V0UXVlcnkuZmlsdGVyLFxuICAgICAgZmllbGRzOiBnZXRRdWVyeS5maWVsZHNcbiAgICB9KTtcbiAgICAvLyBwcmVwYXJlIGEgcXVlcnkgZm9yIHRoZSBuZXh0IHBhZ2Ugb2YgZGF0YSB0byBsb2FkXG4gICAgb3B0aW9ucy5vZmZzZXQgPSAoZ2V0UXVlcnkub2Zmc2V0IHwgMCkgKyBjb2xsZWN0aW9uLm1vZGVscy5sZW5ndGg7XG4gICAgLy8gdGhpcyBtdXN0IGJlIHNldCBpbiBvcHRpb25zIHRvIHN0YXRlIHdlIGhhbmRsZSBpdFxuICAgIG9wdGlvbnMuc3luY0NvbnRleHQgPSB0aGlzO1xuXG4gICAgLy8gc2V0dXAgY2FsbGJhY2tzIGhhbmRsaW5nIHByb2Nlc3Npbmcgb2YgcmVzdWx0cywgZG8gbm90IHVzZSBwcm9taXNlcyBhcyB0aGVzZSBleGVjdXRlIHRvbyBsYXRlLi4uXG4gICAgLy8gTm90aWNlLCBzaW5jZSB3ZSBjYWxsIGNvbGxlY3Rpb24uc3luYygpIGRpcmVjdGx5LCB0aGUgc2lnbmF0dXJlIG9mIHN1Y2Nlc3MvZXJyb3IgY2FsbGJhY2tzIGhlcmUgaXMgYWpheC1zdHlsZS5cbiAgICAvLyBIb3dldmVyLCB0aGUgdXNlci1wcm92aWRlZCBjYWxsYmFja3MgYXJlIHRvIGJlaW5nIGNhbGxlZCBiYWNrYm9uZS5qcy1zdHlsZSB3aXRoIGNvbGxlY3Rpb24gYW5kIG9iamVjdC5cbiAgICB2YXIgb2xkU3VjY2VzcyA9IG9wdGlvbnMuc3VjY2VzcztcbiAgICB2YXIgb2xkRXJyb3IgPSBvcHRpb25zLmVycm9yO1xuICAgIG9wdGlvbnMuc3VjY2VzcyA9IGZ1bmN0aW9uIGZldGNoTW9yZVN1Y2Nlc3MobW9kZWxzOiBhbnlbXSkge1xuICAgICAgLy8gcmVzdG9yZSBjYWxsYmFja3NcbiAgICAgIG9wdGlvbnMuc3VjY2VzcyA9IG9sZFN1Y2Nlc3M7XG4gICAgICBvcHRpb25zLmVycm9yID0gb2xkRXJyb3I7XG5cbiAgICAgIC8vIHVwZGF0ZSBtb2RlbHNcbiAgICAgIGlmIChtb2RlbHMpIHtcbiAgICAgICAgLy8gYWRkIG1vZGVscyB0byBjb2xsZWN0aW9uLCBpZiBhbnlcbiAgICAgICAgaWYgKG1vZGVscy5sZW5ndGggPD0gMCkge1xuICAgICAgICAgIC8vIHJlYWNoZWQgdGhlIGVuZFxuICAgICAgICAgIGRlbGV0ZSBvcHRpb25zLm1vcmU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gcmVhZCBhZGRpdGlvbmFsIGRhdGFcbiAgICAgICAgICBpZiAob3B0aW9ucy5zeW5jQ29udGV4dC5jb21wYXJlRm4pIHtcbiAgICAgICAgICAgIC8vIG5vdGljZSwgZXhpc3RpbmcgcmFuZ2Ugb2YgbW9kZWxzIGlzIHNvcnRlZCBieSBkZWZpbml0aW9uIGFscmVhZHlcbiAgICAgICAgICAgIG9wdGlvbnMuYXQgPSBvcHRpb25zLnN5bmNDb250ZXh0Lmluc2VydGlvblBvaW50KG1vZGVsc1swXSwgY29sbGVjdGlvbi5tb2RlbHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBtb2RlbHMgPSA8YW55PmNvbGxlY3Rpb24uYWRkKG1vZGVscywgb3B0aW9ucykgfHwgbW9kZWxzO1xuXG4gICAgICAgICAgLy8gYWRqdXN0IHF1ZXJ5IHBhcmFtZXRlclxuICAgICAgICAgIGdldFF1ZXJ5LmxpbWl0ID0gY29sbGVjdGlvbi5tb2RlbHMubGVuZ3RoO1xuICAgICAgICAgIGlmIChvcHRpb25zLnN5bmNDb250ZXh0LmdldFF1ZXJ5LmxpbWl0ID4gZ2V0UXVlcnkubGltaXQpIHtcbiAgICAgICAgICAgIC8vIHJlYWNoZWQgdGhlIGVuZFxuICAgICAgICAgICAgZGVsZXRlIG9wdGlvbnMubW9yZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gbW9yZSBkYXRhIHRvIGxvYWRcbiAgICAgICAgICAgIG9wdGlvbnMubW9yZSA9IHRydWU7XG4gICAgICAgICAgICBkZWxldGUgb3B0aW9ucy5lbmQ7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gcmVhY2hlZCB0aGUgZW5kP1xuICAgICAgICBpZiAoIW9wdGlvbnMubW9yZSkge1xuICAgICAgICAgIGdldFF1ZXJ5LmxpbWl0ID0gdW5kZWZpbmVkOyAvLyBvcGVuIGVuZFxuICAgICAgICAgIG9wdGlvbnMuZW5kID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyByZXN0b3JlIHF1ZXJ5IHBhcmFtZXRlclxuICAgICAgb3B0aW9ucy5zeW5jQ29udGV4dC5nZXRRdWVyeSA9IGdldFF1ZXJ5O1xuXG4gICAgICAvLyBjYWxsIHVzZXIgc3VjY2VzcyBjYWxsYmFja1xuICAgICAgaWYgKG9wdGlvbnMuc3VjY2Vzcykge1xuICAgICAgICBtb2RlbHMgPSBvcHRpb25zLnN1Y2Nlc3MuY2FsbCh0aGlzLCBjb2xsZWN0aW9uLCBtb2RlbHMsIG9wdGlvbnMpIHx8IG1vZGVscztcbiAgICAgIH1cbiAgICAgIHJldHVybiBtb2RlbHM7XG4gICAgfTtcbiAgICBvcHRpb25zLmVycm9yID0gZnVuY3Rpb24gZmV0Y2hNb3JlRXJyb3IoZXJyb3I6IEVycm9yKSB7XG4gICAgICAvLyByZXN0b3JlIGNhbGxiYWNrc1xuICAgICAgb3B0aW9ucy5zdWNjZXNzID0gb2xkU3VjY2VzcztcbiAgICAgIG9wdGlvbnMuZXJyb3IgPSBvbGRFcnJvcjtcblxuICAgICAgLy8gcmVzdG9yZSBxdWVyeSBwYXJhbWV0ZXJcbiAgICAgIG9wdGlvbnMuc3luY0NvbnRleHQuZ2V0UXVlcnkgPSBnZXRRdWVyeTtcblxuICAgICAgLy8gY2FsbCB1c2VyIGVycm9yIGNhbGxiYWNrXG4gICAgICBpZiAob3B0aW9ucy5lcnJvcikge1xuICAgICAgICBlcnJvciA9IG9wdGlvbnMuZXJyb3IuY2FsbCh0aGlzLCBjb2xsZWN0aW9uLCBlcnJvciwgb3B0aW9ucykgfHwgZXJyb3I7XG4gICAgICB9XG4gICAgICByZXR1cm4gZXJyb3I7XG4gICAgfTtcblxuICAgIC8vIGZpcmUgdXAgdGhlIHBhZ2UgbG9hZFxuICAgIHRoaXMuZ2V0UXVlcnkgPSBuZXcgR2V0UXVlcnkoZ2V0UXVlcnkpO1xuICAgIHRoaXMuZ2V0UXVlcnkubGltaXQgPSBnZXRRdWVyeS5saW1pdCArIG9wdGlvbnMubGltaXQ7XG4gICAgcmV0dXJuIGNvbGxlY3Rpb24uc3luYyhvcHRpb25zLm1ldGhvZCB8fCAncmVhZCcsIGNvbGxlY3Rpb24sIG9wdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIHJlYWRzIGEgcGFnZSBvZiBkYXRhIGludG8gdGhlIGNvbGxlY3Rpb24uXG4gICAqXG4gICAqIFdoZW4gYXN5bmMgcHJvY2Vzc2luZyBpcyBkb25lLCBhIG5leHQvcHJldiBhdHRyaWJ1dGUgaXMgc2V0IG9uIHRoZSBvcHRpb25zIG9iamVjdCBpbiBjYXNlIGFkZGl0aW9uYWwgcGFnZXMgbWlnaHRcbiAgICogYmUgYXZhaWxhYmxlIHdoaWNoIGNhbiBiZSBsb2FkZWQgYnkgY2FsbGluZyB0aGlzIG1ldGhvZCBhZ2Fpbi5cbiAgICpcbiAgICogQHBhcmFtIHtvYmplY3R9IGNvbGxlY3Rpb24gdG8gbG9hZCBkYXRhIGludG8uXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIGluY2wuIG9mZnNldCBhbmQgbGltaXQgb2YgcGFnZSB0byByZXRyaWV2ZS5cbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSBvZiB0aGUgbG9hZCBvcGVyYXRpb24uXG4gICAqL1xuICBwcml2YXRlIGZldGNoUmFuZ2UoY29sbGVjdGlvbjogQ29sbGVjdGlvbiwgb3B0aW9uczogYW55ID0ge30pIHtcbiAgICAvLyB0aGlzIG11c3QgYmUgc2V0IGluIG9wdGlvbnMgdG8gc3RhdGUgd2UgaGFuZGxlIGl0XG4gICAgb3B0aW9ucy5zeW5jQ29udGV4dCA9IHRoaXM7XG5cbiAgICAvLyBwcmVwYXJlIGEgcXVlcnkgZm9yIHRoZSBwYWdlIG9mIGRhdGEgdG8gbG9hZFxuICAgIHZhciBvbGRRdWVyeSA9IHRoaXMuZ2V0UXVlcnk7XG4gICAgdmFyIG5ld1F1ZXJ5ID0gbmV3IEdldFF1ZXJ5KG9sZFF1ZXJ5KTtcbiAgICBpZiAob3B0aW9ucy5vZmZzZXQgPj0gMCkge1xuICAgICAgbmV3UXVlcnkub2Zmc2V0ID0gb3B0aW9ucy5vZmZzZXQ7XG4gICAgfSBlbHNlIGlmIChvcHRpb25zLm9mZnNldCA8IDApIHtcbiAgICAgIG5ld1F1ZXJ5Lm9mZnNldCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgdmFyIG9sZExpbWl0ID0gb3B0aW9ucy5saW1pdDtcbiAgICBpZiAob3B0aW9ucy5saW1pdCA+IDApIHtcbiAgICAgIG5ld1F1ZXJ5LmxpbWl0ID0gb3B0aW9ucy5saW1pdCArIDE7XG4gICAgICBvcHRpb25zLmxpbWl0ID0gbmV3UXVlcnkubGltaXQ7XG4gICAgfSBlbHNlIGlmIChvcHRpb25zLmxpbWl0IDw9IDApIHtcbiAgICAgIG5ld1F1ZXJ5LmxpbWl0ID0gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIC8vIHNldHVwIGNhbGxiYWNrcyBoYW5kbGluZyBwcm9jZXNzaW5nIG9mIHJlc3VsdHMsIGRvIG5vdCB1c2UgcHJvbWlzZXMgYXMgdGhlc2UgZXhlY3V0ZSB0b28gbGF0ZS4uLlxuICAgIC8vIE5vdGljZSwgc2luY2Ugd2UgY2FsbCBjb2xsZWN0aW9uLnN5bmMoKSBkaXJlY3RseSwgdGhlIHNpZ25hdHVyZSBvZiBzdWNjZXNzL2Vycm9yIGNhbGxiYWNrcyBoZXJlIGlzIGFqYXgtc3R5bGUuXG4gICAgLy8gSG93ZXZlciwgdGhlIHVzZXItcHJvdmlkZWQgY2FsbGJhY2tzIGFyZSB0byBiZWluZyBjYWxsZWQgYmFja2JvbmUuanMtc3R5bGUgd2l0aCBjb2xsZWN0aW9uIGFuZCBvYmplY3QuXG4gICAgdmFyIG9sZFN1Y2Nlc3MgPSBvcHRpb25zLnN1Y2Nlc3M7XG4gICAgdmFyIG9sZEVycm9yID0gb3B0aW9ucy5lcnJvcjtcbiAgICBvcHRpb25zLnN1Y2Nlc3MgPSBmdW5jdGlvbiBmZXRjaFJhbmdlU3VjY2Vzcyhtb2RlbHM6IGFueVtdKSB7XG4gICAgICAvLyByZXN0b3JlIGNhbGxiYWNrcyBhbmQgbGltaXRcbiAgICAgIG9wdGlvbnMuc3VjY2VzcyA9IG9sZFN1Y2Nlc3M7XG4gICAgICBvcHRpb25zLmVycm9yID0gb2xkRXJyb3I7XG4gICAgICBpZiAob2xkTGltaXQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBvcHRpb25zLmxpbWl0ID0gb2xkTGltaXQ7XG4gICAgICB9XG5cbiAgICAgIC8vIHVwZGF0ZSBtb2RlbHNcbiAgICAgIGlmIChtb2RlbHMpIHtcbiAgICAgICAgLy8gYWRkIG1vZGVscyB0byBjb2xsZWN0aW9uLCBpZiBhbnlcbiAgICAgICAgaWYgKG1vZGVscy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgLy8gYWRqdXN0IHF1ZXJ5IHBhcmFtZXRlclxuICAgICAgICAgIG9wdGlvbnMubmV4dCA9IG5ld1F1ZXJ5LmxpbWl0ICYmIG1vZGVscy5sZW5ndGggPj0gbmV3UXVlcnkubGltaXQ7XG4gICAgICAgICAgaWYgKG9wdGlvbnMubmV4dCkge1xuICAgICAgICAgICAgLy8gdHJpY2sgaGVyZSB3YXMgdG8gcmVhZCBvbmUgbW9yZSBpdGVtIHRvIHNlZSBpZiB0aGVyZSBpcyBtb3JlIHRvIGNvbWVcbiAgICAgICAgICAgIG1vZGVscy5sZW5ndGggPSBtb2RlbHMubGVuZ3RoIC0gMTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyByZWFsaXplIHRoZSBwYWdlXG4gICAgICAgICAgbW9kZWxzID0gY29sbGVjdGlvbi5yZXNldChtb2RlbHMsIG9wdGlvbnMpIHx8IG1vZGVscztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyByZWFjaGVkIHRoZSBlbmRcbiAgICAgICAgICBkZWxldGUgb3B0aW9ucy5uZXh0O1xuICAgICAgICB9XG4gICAgICAgIG9wdGlvbnMucHJldiA9IG5ld1F1ZXJ5Lm9mZnNldCA+IDA7XG4gICAgICB9XG5cbiAgICAgIC8vIGNhbGwgdXNlciBzdWNjZXNzIGNhbGxiYWNrXG4gICAgICBpZiAob3B0aW9ucy5zdWNjZXNzKSB7XG4gICAgICAgIG1vZGVscyA9IG9wdGlvbnMuc3VjY2Vzcy5jYWxsKHRoaXMsIGNvbGxlY3Rpb24sIG1vZGVscywgb3B0aW9ucykgfHwgbW9kZWxzO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG1vZGVscztcbiAgICB9O1xuICAgIG9wdGlvbnMuZXJyb3IgPSBmdW5jdGlvbiBmZXRjaE1vcmVFcnJvcihlcnJvcjogRXJyb3IpIHtcbiAgICAgIC8vIHJlc3RvcmUgY2FsbGJhY2tzIGFuZCBsaW1pdFxuICAgICAgb3B0aW9ucy5zdWNjZXNzID0gb2xkU3VjY2VzcztcbiAgICAgIG9wdGlvbnMuZXJyb3IgPSBvbGRFcnJvcjtcbiAgICAgIGlmIChvbGRMaW1pdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIG9wdGlvbnMubGltaXQgPSBvbGRMaW1pdDtcbiAgICAgIH1cblxuICAgICAgLy8gcmVzdG9yZSBxdWVyeSBwYXJhbWV0ZXJcbiAgICAgIG9wdGlvbnMuc3luY0NvbnRleHQuZ2V0UXVlcnkgPSBvbGRRdWVyeTtcblxuICAgICAgLy8gY2FsbCB1c2VyIGVycm9yIGNhbGxiYWNrXG4gICAgICBpZiAob3B0aW9ucy5lcnJvcikge1xuICAgICAgICBlcnJvciA9IG9wdGlvbnMuZXJyb3IuY2FsbCh0aGlzLCBjb2xsZWN0aW9uLCBlcnJvciwgb3B0aW9ucykgfHwgZXJyb3I7XG4gICAgICB9XG4gICAgICByZXR1cm4gZXJyb3I7XG4gICAgfTtcblxuICAgIC8vIGZpcmUgdXAgdGhlIHBhZ2UgbG9hZFxuICAgIHRoaXMuZ2V0UXVlcnkgPSBuZXdRdWVyeTtcbiAgICByZXR1cm4gY29sbGVjdGlvbi5zeW5jKG9wdGlvbn