breeze-client
Version:
Breeze data management for JavaScript clients
264 lines (254 loc) • 31.9 kB
JavaScript
import { core } from './core';
import { assertParam } from './assert-param';
function publishCore(that, data, errorCallback) {
let subscribers = that._subscribers;
if (!subscribers)
return true;
// subscribers from outer scope.
subscribers.forEach(function (s) {
try {
s.callback(data);
}
catch (e) {
e.context = "unable to publish on topic: " + that.name;
if (errorCallback) {
errorCallback(e);
}
else if (that._defaultErrorCallback) {
that._defaultErrorCallback(e);
}
else {
fallbackErrorHandler(e);
}
}
});
}
function fallbackErrorHandler(e) {
// TODO: maybe log this
// for now do nothing;
}
/**
Class to support basic event publication and subscription semantics.
@dynamic
**/
export class BreezeEvent {
/** @hidden @internal */
static __eventNameMap = {};
/** @hidden @internal */
static __nextUnsubKey = 1;
/** The name of this Event */
name;
/** The object doing the publication. i.e. the object to which this event is attached. */
publisher;
/** @hidden @internal */
_subscribers;
/** @hidden @internal */
_defaultErrorCallback;
/**
Constructor for an Event
> salaryEvent = new BreezeEvent("salaryEvent", person);
@param name - The name of the event.
@param publisher - The object that will be doing the publication. i.e. the object to which this event is attached.
@param defaultErrorCallback - Function to call when an error occurs during subscription execution.
If omitted then subscriber notification failures will be ignored.
**/
constructor(name, publisher, defaultErrorCallback) {
assertParam(name, "eventName").isNonEmptyString().check();
assertParam(publisher, "publisher").isObject().check();
this.name = name;
// register the name
BreezeEvent.__eventNameMap[name] = true;
this.publisher = publisher;
if (defaultErrorCallback) {
this._defaultErrorCallback = defaultErrorCallback;
}
}
/**
Publish data for this event.
> // Assume 'salaryEvent' is previously constructed Event
> salaryEvent.publish( { eventType: "payRaise", amount: 100 });
This event can also be published asychronously
> salaryEvent.publish( { eventType: "payRaise", amount: 100 }, true);
And we can add a handler in case the subscriber 'mishandles' the event.
> salaryEvent.publish( { eventType: "payRaise", amount: 100 }, true, function(error) {
> // do something with the 'error' object
> });
@param data - Data to publish
@param publishAsync - (default=false) Whether to publish asynchonously or not.
@param errorCallback - Function to be called for any errors that occur during publication. If omitted,
errors will be eaten.
@return false if event is disabled; true otherwise.
**/
publish(data, publishAsync = false, errorCallback) {
if (!BreezeEvent._isEnabled(this.name, this.publisher))
return false;
if (publishAsync === true) {
setTimeout(publishCore, 0, this, data, errorCallback);
}
else {
publishCore(this, data, errorCallback);
}
return true;
}
/**
Publish data for this event asynchronously.
> // Assume 'salaryEvent' is previously constructed Event
> salaryEvent.publishAsync( { eventType: "payRaise", amount: 100 });
And we can add a handler in case the subscriber 'mishandles' the event.
> salaryEvent.publishAsync( { eventType: "payRaise", amount: 100 }, function(error) {
> // do something with the 'error' object
> });
@param data - Data to publish
@param errorCallback - Function to be called for any errors that occur during publication. If omitted,
errors will be eaten.
**/
publishAsync(data, errorCallback) {
this.publish(data, true, errorCallback);
}
/**
Subscribe to this event.
> // Assume 'salaryEvent' is previously constructed Event
> salaryEvent.subscribe(function (eventArgs) {
> if (eventArgs.eventType === "payRaise") {
> // do something
> }
> });
There are several built in Breeze events, such as [[EntityAspect.propertyChanged]], [[EntityAspect.validationErrorsChanged]] as well.
> // Assume order is a preexisting 'order' entity
> order.entityAspect.propertyChanged.subscribe(function (pcEvent) {
> if ( pcEvent.propertyName === "OrderDate") {
> // do something
> }
> });
@param callback- Function to be called whenever 'data' is published for this event.
@param callback.data - {Object} Whatever 'data' was published. This should be documented on the specific event.
@return This is a key for 'unsubscription'. It can be passed to the 'unsubscribe' method.
**/
subscribe(callback) {
if (!this._subscribers) {
this._subscribers = [];
}
let unsubKey = BreezeEvent.__nextUnsubKey;
this._subscribers.push({ unsubKey: unsubKey, callback: callback });
++BreezeEvent.__nextUnsubKey;
return unsubKey;
}
/**
Unsubscribe from this event.
> // Assume order is a preexisting 'order' entity
> let token = order.entityAspect.propertyChanged.subscribe(function (pcEvent) {
> // do something
> });
> // sometime later
> order.entityAspect.propertyChanged.unsubscribe(token);
@param unsubKey - The value returned from the 'subscribe' method may be used to unsubscribe here.
@return Whether unsubscription occured. This will return false if already unsubscribed or if the key simply
cannot be found.
**/
unsubscribe = function (unsubKey) {
if (!this._subscribers)
return false;
let subs = this._subscribers;
let ix = core.arrayIndexOf(subs, function (s) {
return s.unsubKey === unsubKey;
});
if (ix !== -1) {
subs.splice(ix, 1);
if (subs.length === 0) {
this._subscribers = null;
}
return true;
}
else {
return false;
}
};
/** remove all subscribers */
clear() {
this._subscribers = null;
}
/** event bubbling - document later. */
// null or undefined 'getParentFn' means Event does not need to bubble i.e. that it is always enabled - .
static bubbleEvent(target, getParentFn) {
target._getEventParent = getParentFn || null;
}
/**
Enables or disables the named event for an object and all of its children.
> BreezeEvent.enable(“propertyChanged”, myEntityManager, false)
will disable all EntityAspect.propertyChanged events within a EntityManager.
> BreezeEvent.enable(“propertyChanged”, myEntityManager, true)
will enable all EntityAspect.propertyChanged events within a EntityManager.
> BreezeEvent.enable(“propertyChanged”, myEntity.entityAspect, false)
will disable EntityAspect.propertyChanged events for a specific entity.
> BreezeEvent.enable(“propertyChanged”, myEntity.entityAspect, null)
will removes any enabling / disabling at the entity aspect level so now any 'Event.enable' calls at the EntityManager level,
made either previously or in the future, will control notification.
> BreezeEvent.enable(“validationErrorsChanged”, myEntityManager, function(em) {
> return em.customTag === “blue”;
> })
will either enable or disable myEntityManager based on the current value of a ‘customTag’ property on myEntityManager.
Note that this is dynamic, changing the customTag value will cause events to be enabled or disabled immediately.
@param eventName - The name of the event.
@param target - The object at which enabling or disabling will occur. All event notifications that occur to this object or
children of this object will be enabled or disabled.
@param isEnabled - A boolean, a null or a function that returns either a boolean or a null.
**/
static enable(eventName, obj, isEnabled) {
assertParam(eventName, "eventName").isNonEmptyString().check();
assertParam(obj, "obj").isObject().check();
assertParam(isEnabled, "isEnabled").isBoolean().isOptional().or().isFunction().check();
let ob = obj;
if (!ob._$eventMap) {
ob._$eventMap = {};
}
ob._$eventMap[eventName] = isEnabled;
}
/**
Returns whether for a specific event and a specific object and its children, notification is enabled or disabled or not set.
> BreezeEvent.isEnabled(“propertyChanged”, myEntityManager)
>
@param eventName - The name of the event.
@param target - The object for which we want to know if notifications are enabled.
@return A null is returned if this value has not been set.
**/
static isEnabled(eventName, obj) {
assertParam(eventName, "eventName").isNonEmptyString().check();
assertParam(obj, "obj").isObject().check();
// null is ok - it just means that the object is at the top level.
if (obj._getEventParent === undefined) {
throw new Error("This object does not support event enabling/disabling");
}
// return ctor._isEnabled(getFullEventName(eventName), obj);
return BreezeEvent._isEnabled(eventName, 3);
}
/** @hidden @internal */
static _isEnabled = function (eventName, obj) {
let isEnabled = null;
let ob = obj;
let eventMap = ob._$eventMap;
if (eventMap) {
isEnabled = eventMap[eventName];
}
if (isEnabled != null) {
if (typeof isEnabled === 'function') {
return !!isEnabled(obj);
}
else {
return !!isEnabled;
}
}
else {
let parent = ob._getEventParent && ob._getEventParent();
if (parent) {
return !!this._isEnabled(eventName, parent);
}
else {
// default if not explicitly disabled.
return true;
}
}
};
}
// legacy support - deliberately not typed
core.Event = BreezeEvent;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXZlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUM5QixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFN0MsU0FBUyxXQUFXLENBQUksSUFBb0IsRUFBRSxJQUFPLEVBQUUsYUFBaUM7SUFDdEYsSUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQztJQUNwQyxJQUFJLENBQUMsV0FBVztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzlCLGdDQUFnQztJQUNoQyxXQUFXLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztRQUM3QixJQUFJO1lBQ0YsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNsQjtRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1YsQ0FBQyxDQUFDLE9BQU8sR0FBRyw4QkFBOEIsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO1lBQ3ZELElBQUksYUFBYSxFQUFFO2dCQUNqQixhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDbEI7aUJBQU0sSUFBSSxJQUFJLENBQUMscUJBQXFCLEVBQUU7Z0JBQ3JDLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUMvQjtpQkFBTTtnQkFDTCxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUN6QjtTQUNGO0lBQ0gsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyxvQkFBb0IsQ0FBQyxDQUFRO0lBQ3BDLHVCQUF1QjtJQUN2QixzQkFBc0I7QUFDeEIsQ0FBQztBQVNEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxXQUFXO0lBQ3RCLHdCQUF3QjtJQUN4QixNQUFNLENBQUMsY0FBYyxHQUFHLEVBQUUsQ0FBQztJQUMzQix3QkFBd0I7SUFDeEIsTUFBTSxDQUFDLGNB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Q3pDLElBQVksQ0FBQyxLQUFLLEdBQUcsV0FBVyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY29yZSB9IGZyb20gJy4vY29yZSc7XHJcbmltcG9ydCB7IGFzc2VydFBhcmFtIH0gZnJvbSAnLi9hc3NlcnQtcGFyYW0nO1xyXG5cclxuZnVuY3Rpb24gcHVibGlzaENvcmU8VD4odGhhdDogQnJlZXplRXZlbnQ8VD4sIGRhdGE6IFQsIGVycm9yQ2FsbGJhY2s/OiAoZTogRXJyb3IpID0+IGFueSkge1xyXG4gIGxldCBzdWJzY3JpYmVycyA9IHRoYXQuX3N1YnNjcmliZXJzO1xyXG4gIGlmICghc3Vic2NyaWJlcnMpIHJldHVybiB0cnVlO1xyXG4gIC8vIHN1YnNjcmliZXJzIGZyb20gb3V0ZXIgc2NvcGUuXHJcbiAgc3Vic2NyaWJlcnMuZm9yRWFjaChmdW5jdGlvbiAocykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcy5jYWxsYmFjayhkYXRhKTtcclxuICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgZS5jb250ZXh0ID0gXCJ1bmFibGUgdG8gcHVibGlzaCBvbiB0b3BpYzogXCIgKyB0aGF0Lm5hbWU7XHJcbiAgICAgIGlmIChlcnJvckNhbGxiYWNrKSB7XHJcbiAgICAgICAgZXJyb3JDYWxsYmFjayhlKTtcclxuICAgICAgfSBlbHNlIGlmICh0aGF0Ll9kZWZhdWx0RXJyb3JDYWxsYmFjaykge1xyXG4gICAgICAgIHRoYXQuX2RlZmF1bHRFcnJvckNhbGxiYWNrKGUpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGZhbGxiYWNrRXJyb3JIYW5kbGVyKGUpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGZhbGxiYWNrRXJyb3JIYW5kbGVyKGU6IEVycm9yKSB7XHJcbiAgLy8gVE9ETzogbWF5YmUgbG9nIHRoaXNcclxuICAvLyBmb3Igbm93IGRvIG5vdGhpbmc7XHJcbn1cclxuXHJcblxyXG4vKiogQGhpZGRlbiBAaW50ZXJuYWwgKi9cclxuZXhwb3J0IGludGVyZmFjZSBTdWJzY3JpcHRpb24ge1xyXG4gIHVuc3ViS2V5OiBudW1iZXI7XHJcbiAgY2FsbGJhY2s6IChkYXRhOiBhbnkpID0+IGFueTtcclxufVxyXG5cclxuLyoqXHJcbkNsYXNzIHRvIHN1cHBvcnQgYmFzaWMgZXZlbnQgcHVibGljYXRpb24gYW5kIHN1YnNjcmlwdGlvbiBzZW1hbnRpY3MuXHJcbkBkeW5hbWljXHJcbioqL1xyXG5leHBvcnQgY2xhc3MgQnJlZXplRXZlbnQ8VD4ge1xyXG4gIC8qKiBAaGlkZGVuIEBpbnRlcm5hbCAqL1xyXG4gIHN0YXRpYyBfX2V2ZW50TmFtZU1hcCA9IHt9O1xyXG4gIC8qKiBAaGlkZGVuIEBpbnRlcm5hbCAqL1xyXG4gIHN0YXRpYyBfX25leHRVbnN1YktleSA9IDE7XHJcbiAgLyoqIFRoZSBuYW1lIG9mIHRoaXMgRXZlbnQgKi9cclxuICBuYW1lOiBzdHJpbmc7XHJcbiAgLyoqIFRoZSBvYmplY3QgZG9pbmcgdGhlIHB1YmxpY2F0aW9uLiBpLmUuIHRoZSBvYmplY3QgdG8gd2hpY2ggdGhpcyBldmVudCBpcyBhdHRhY2hlZC4gKi9cclxuICBwdWJsaXNoZXI6IE9iamVjdDtcclxuXHJcbiAgLyoqIEBoaWRkZW4gQGludGVybmFsICovXHJcbiAgX3N1YnNjcmliZXJzOiBTdWJzY3JpcHRpb25bXTtcclxuICAvKiogQGhpZGRlbiBAaW50ZXJuYWwgKi9cclxuICBfZGVmYXVsdEVycm9yQ2FsbGJhY2s6IChlOiBFcnJvcikgPT4gYW55O1xyXG5cclxuXHJcbiAgLyoqXHJcbiAgQ29uc3RydWN0b3IgZm9yIGFuIEV2ZW50XHJcbiAgPiAgICAgc2FsYXJ5RXZlbnQgPSBuZXcgQnJlZXplRXZlbnQoXCJzYWxhcnlFdmVudFwiLCBwZXJzb24pO1xyXG4gIEBwYXJhbSBuYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGV2ZW50LlxyXG4gIEBwYXJhbSBwdWJsaXNoZXIgLSBUaGUgb2JqZWN0IHRoYXQgd2lsbCBiZSBkb2luZyB0aGUgcHVibGljYXRpb24uIGkuZS4gdGhlIG9iamVjdCB0byB3aGljaCB0aGlzIGV2ZW50IGlzIGF0dGFjaGVkLlxyXG4gIEBwYXJhbSBkZWZhdWx0RXJyb3JDYWxsYmFjayAtIEZ1bmN0aW9uIHRvIGNhbGwgd2hlbiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nIHN1YnNjcmlwdGlvbiBleGVjdXRpb24uIFxyXG4gIElmIG9taXR0ZWQgdGhlbiBzdWJzY3JpYmVyIG5vdGlmaWNhdGlvbiBmYWlsdXJlcyB3aWxsIGJlIGlnbm9yZWQuXHJcbiAgKiovXHJcbiAgY29uc3RydWN0b3IobmFtZTogc3RyaW5nLCBwdWJsaXNoZXI6IE9iamVjdCwgZGVmYXVsdEVycm9yQ2FsbGJhY2s/OiAoZTogRXJyb3IpID0+IGFueSkge1xyXG4gICAgYXNzZXJ0UGFyYW0obmFtZSwgXCJldmVudE5hbWVcIikuaXNOb25FbXB0eVN0cmluZygpLmNoZWNrKCk7XHJcbiAgICBhc3NlcnRQYXJhbShwdWJsaXNoZXIsIFwicHVibGlzaGVyXCIpLmlzT2JqZWN0KCkuY2hlY2soKTtcclxuXHJcbiAgICB0aGlzLm5hbWUgPSBuYW1lO1xyXG4gICAgLy8gcmVnaXN0ZXIgdGhlIG5hbWVcclxuICAgIEJyZWV6ZUV2ZW50Ll9fZXZlbnROYW1lTWFwW25hbWVdID0gdHJ1ZTtcclxuICAgIHRoaXMucHVibGlzaGVyID0gcHVibGlzaGVyO1xyXG4gICAgaWYgKGRlZmF1bHRFcnJvckNhbGxiYWNrKSB7XHJcbiAgICAgIHRoaXMuX2RlZmF1bHRFcnJvckNhbGxiYWNrID0gZGVmYXVsdEVycm9yQ2FsbGJhY2s7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICBQdWJsaXNoIGRhdGEgZm9yIHRoaXMgZXZlbnQuXHJcbiAgPiAgICAgIC8vIEFzc3VtZSAnc2FsYXJ5RXZlbnQnIGlzIHByZXZpb3VzbHkgY29uc3RydWN0ZWQgRXZlbnRcclxuICA+ICAgICAgc2FsYXJ5RXZlbnQucHVibGlzaCggeyBldmVudFR5cGU6IFwicGF5UmFpc2VcIiwgYW1vdW50OiAxMDAgfSk7XHJcblxyXG4gIFRoaXMgZXZlbnQgY2FuIGFsc28gYmUgcHVibGlzaGVkIGFzeWNocm9ub3VzbHlcclxuICA+ICAgICAgc2FsYXJ5RXZlbnQucHVibGlzaCggeyBldmVudFR5cGU6IFwicGF5UmFpc2VcIiwgYW1vdW50OiAxMDAgfSwgdHJ1ZSk7XHJcblxyXG4gIEFuZCB3ZSBjYW4gYWRkIGEgaGFuZGxlciBpbiBjYXNlIHRoZSBzdWJzY3JpYmVyICdtaXNoYW5kbGVzJyB0aGUgZXZlbnQuXHJcbiAgPiAgICAgIHNhbGFyeUV2ZW50LnB1Ymxpc2goIHsgZXZlbnRUeXBlOiBcInBheVJhaXNlXCIsIGFtb3VudDogMTAwIH0sIHRydWUsIGZ1bmN0aW9uKGVycm9yKSB7XHJcbiAgPiAgICAgICAgICAvLyBkbyBzb21ldGhpbmcgd2l0aCB0aGUgJ2Vycm9yJyBvYmplY3RcclxuICA+ICAgICAgfSk7XHJcbiAgQHBhcmFtIGRhdGEgLSBEYXRhIHRvIHB1Ymxpc2hcclxuICBAcGFyYW0gcHVibGlzaEFzeW5jIC0gKGRlZmF1bHQ9ZmFsc2UpIFdoZXRoZXIgdG8gcHVibGlzaCBhc3luY2hvbm91c2x5IG9yIG5vdC5cclxuICBAcGFyYW0gZXJyb3JDYWxsYmFjayAtIEZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBmb3IgYW55IGVycm9ycyB0aGF0IG9jY3VyIGR1cmluZyBwdWJsaWNhdGlvbi4gSWYgb21pdHRlZCxcclxuICBlcnJvcnMgd2lsbCBiZSBlYXRlbi5cclxuICBAcmV0dXJuIGZhbHNlIGlmIGV2ZW50IGlzIGRpc2FibGVkOyB0cnVlIG90aGVyd2lzZS5cclxuICAqKi9cclxuICBwdWJsaXNoKGRhdGE6IFQsIHB1Ymxpc2hBc3luYzogYm9vbGVhbiA9IGZhbHNlLCBlcnJvckNhbGxiYWNrPzogKGU6IEVycm9yKSA9PiBhbnkpIHtcclxuXHJcbiAgICBpZiAoIUJyZWV6ZUV2ZW50Ll9pc0VuYWJsZWQodGhpcy5uYW1lLCB0aGlzLnB1Ymxpc2hlcikpIHJldHVybiBmYWxzZTtcclxuXHJcbiAgICBpZiAocHVibGlzaEFzeW5jID09PSB0cnVlKSB7XHJcbiAgICAgIHNldFRpbWVvdXQocHVibGlzaENvcmUsIDAsIHRoaXMsIGRhdGEsIGVycm9yQ2FsbGJhY2spO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgcHVibGlzaENvcmUodGhpcywgZGF0YSwgZXJyb3JDYWxsYmFjayk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gIFB1Ymxpc2ggZGF0YSBmb3IgdGhpcyBldmVudCBhc3luY2hyb25vdXNseS5cclxuICA+ICAgICAgLy8gQXNzdW1lICdzYWxhcnlFdmVudCcgaXMgcHJldmlvdXNseSBjb25zdHJ1Y3RlZCBFdmVudFxyXG4gID4gICAgICBzYWxhcnlFdmVudC5wdWJsaXNoQXN5bmMoIHsgZXZlbnRUeXBlOiBcInBheVJhaXNlXCIsIGFtb3VudDogMTAwIH0pO1xyXG5cclxuICBBbmQgd2UgY2FuIGFkZCBhIGhhbmRsZXIgaW4gY2FzZSB0aGUgc3Vic2NyaWJlciAnbWlzaGFuZGxlcycgdGhlIGV2ZW50LlxyXG4gID4gICAgICBzYWxhcnlFdmVudC5wdWJsaXNoQXN5bmMoIHsgZXZlbnRUeXBlOiBcInBheVJhaXNlXCIsIGFtb3VudDogMTAwIH0sIGZ1bmN0aW9uKGVycm9yKSB7XHJcbiAgPiAgICAgICAgICAvLyBkbyBzb21ldGhpbmcgd2l0aCB0aGUgJ2Vycm9yJyBvYmplY3RcclxuICA+ICAgICAgfSk7XHJcbiAgQHBhcmFtIGRhdGEgLSBEYXRhIHRvIHB1Ymxpc2hcclxuICBAcGFyYW0gZXJyb3JDYWxsYmFjayAtIEZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBmb3IgYW55IGVycm9ycyB0aGF0IG9jY3VyIGR1cmluZyBwdWJsaWNhdGlvbi4gSWYgb21pdHRlZCxcclxuICBlcnJvcnMgd2lsbCBiZSBlYXRlbi5cclxuICAqKi9cclxuICBwdWJsaXNoQXN5bmMoZGF0YTogVCwgZXJyb3JDYWxsYmFjazogKGU6IEVycm9yKSA9PiBhbnkpIHtcclxuICAgIHRoaXMucHVibGlzaChkYXRhLCB0cnVlLCBlcnJvckNhbGxiYWNrKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gIFN1YnNjcmliZSB0byB0aGlzIGV2ZW50LlxyXG4gID4gICAgICAvLyBBc3N1bWUgJ3NhbGFyeUV2ZW50JyBpcyBwcmV2aW91c2x5IGNvbnN0cnVjdGVkIEV2ZW50XHJcbiAgPiAgICAgIHNhbGFyeUV2ZW50LnN1YnNjcmliZShmdW5jdGlvbiAoZXZlbnRBcmdzKSB7XHJcbiAgPiAgICAgICAgICBpZiAoZXZlbnRBcmdzLmV2ZW50VHlwZSA9PT0gXCJwYXlSYWlzZVwiKSB7XHJcbiAgPiAgICAgICAgICAgICAgLy8gZG8gc29tZXRoaW5nXHJcbiAgPiAgICAgICAgICB9XHJcbiAgPiAgICAgIH0pO1xyXG5cclxuICBUaGVyZSBhcmUgc2V2ZXJhbCBidWlsdCBpbiBCcmVlemUgZXZlbnRzLCBzdWNoIGFzIFtbRW50aXR5QXNwZWN0LnByb3BlcnR5Q2hhbmdlZF1dLCBbW0VudGl0eUFzcGVjdC52YWxpZGF0aW9uRXJyb3JzQ2hhbmdlZF1dIGFzIHdlbGwuXHJcbiAgPiAgICAgIC8vIEFzc3VtZSBvcmRlciBpcyBhIHByZWV4aXN0aW5nICdvcmRlcicgZW50aXR5XHJcbiAgPiAgICAgIG9yZGVyLmVudGl0eUFzcGVjdC5wcm9wZXJ0eUNoYW5nZWQuc3Vic2NyaWJlKGZ1bmN0aW9uIChwY0V2ZW50KSB7XHJcbiAgPiAgICAgICAgICBpZiAoIHBjRXZlbnQucHJvcGVydHlOYW1lID09PSBcIk9yZGVyRGF0ZVwiKSB7XHJcbiAgPiAgICAgICAgICAgICAgLy8gZG8gc29tZXRoaW5nXHJcbiAgPiAgICAgICAgICB9XHJcbiAgPiAgICAgIH0pO1xyXG4gIEBwYXJhbSBjYWxsYmFjay0gRnVuY3Rpb24gdG8gYmUgY2FsbGVkIHdoZW5ldmVyICdkYXRhJyBpcyBwdWJsaXNoZWQgZm9yIHRoaXMgZXZlbnQuXHJcbiAgQHBhcmFtIGNhbGxiYWNrLmRhdGEgLSB7T2JqZWN0fSBXaGF0ZXZlciAnZGF0YScgd2FzIHB1Ymxpc2hlZC4gIFRoaXMgc2hvdWxkIGJlIGRvY3VtZW50ZWQgb24gdGhlIHNwZWNpZmljIGV2ZW50LlxyXG4gIEByZXR1cm4gVGhpcyBpcyBhIGtleSBmb3IgJ3Vuc3Vic2NyaXB0aW9uJy4gIEl0IGNhbiBiZSBwYXNzZWQgdG8gdGhlICd1bnN1YnNjcmliZScgbWV0aG9kLlxyXG4gICoqL1xyXG4gIHN1YnNjcmliZShjYWxsYmFjazogKGRhdGE6IFQpID0+IGFueSkge1xyXG4gICAgaWYgKCF0aGlzLl9zdWJzY3JpYmVycykge1xyXG4gICAgICB0aGlzLl9zdWJzY3JpYmVycyA9IFtdO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCB1bnN1YktleSA9IEJyZWV6ZUV2ZW50Ll9fbmV4dFVuc3ViS2V5O1xyXG4gICAgdGhpcy5fc3Vic2NyaWJlcnMucHVzaCh7IHVuc3ViS2V5OiB1bnN1YktleSwgY2FsbGJhY2s6IGNhbGxiYWNrIH0pO1xyXG4gICAgKytCcmVlemVFdmVudC5fX25leHRVbnN1YktleTtcclxuICAgIHJldHVybiB1bnN1YktleTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gIFVuc3Vic2NyaWJlIGZyb20gdGhpcyBldmVudC5cclxuICA+ICAgICAgLy8gQXNzdW1lIG9yZGVyIGlzIGEgcHJlZXhpc3RpbmcgJ29yZGVyJyBlbnRpdHlcclxuICA+ICAgICAgbGV0IHRva2VuID0gb3JkZXIuZW50aXR5QXNwZWN0LnByb3BlcnR5Q2hhbmdlZC5zdWJzY3JpYmUoZnVuY3Rpb24gKHBjRXZlbnQpIHtcclxuICA+ICAgICAgICAgICAgICAvLyBkbyBzb21ldGhpbmdcclxuICA+ICAgICAgfSk7XHJcbiAgPiAgICAgIC8vIHNvbWV0aW1lIGxhdGVyXHJcbiAgPiAgICAgIG9yZGVyLmVudGl0eUFzcGVjdC5wcm9wZXJ0eUNoYW5nZWQudW5zdWJzY3JpYmUodG9rZW4pO1xyXG4gIEBwYXJhbSB1bnN1YktleSAtIFRoZSB2YWx1ZSByZXR1cm5lZCBmcm9tIHRoZSAnc3Vic2NyaWJlJyBtZXRob2QgbWF5IGJlIHVzZWQgdG8gdW5zdWJzY3JpYmUgaGVyZS5cclxuICBAcmV0dXJuIFdoZXRoZXIgdW5zdWJzY3JpcHRpb24gb2NjdXJlZC4gVGhpcyB3aWxsIHJldHVybiBmYWxzZSBpZiBhbHJlYWR5IHVuc3Vic2NyaWJlZCBvciBpZiB0aGUga2V5IHNpbXBseVxyXG4gIGNhbm5vdCBiZSBmb3VuZC5cclxuICAqKi9cclxuICB1bnN1YnNjcmliZSA9IGZ1bmN0aW9uICh1bnN1YktleTogbnVtYmVyKSB7XHJcbiAgICBpZiAoIXRoaXMuX3N1YnNjcmliZXJzKSByZXR1cm4gZmFsc2U7XHJcbiAgICBsZXQgc3VicyA9IHRoaXMuX3N1YnNjcmliZXJzO1xyXG4gICAgbGV0IGl4ID0gY29yZS5hcnJheUluZGV4T2Yoc3VicywgZnVuY3Rpb24gKHMpIHtcclxuICAgICAgcmV0dXJuIHMudW5zdWJLZXkgPT09IHVuc3ViS2V5O1xyXG4gICAgfSk7XHJcbiAgICBpZiAoaXggIT09IC0xKSB7XHJcbiAgICAgIHN1YnMuc3BsaWNlKGl4LCAxKTtcclxuICAgICAgaWYgKHN1YnMubGVuZ3RoID09PSAwKSB7XHJcbiAgICAgICAgdGhpcy5fc3Vic2NyaWJlcnMgPSBudWxsO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8qKiByZW1vdmUgYWxsIHN1YnNjcmliZXJzICovXHJcbiAgY2xlYXIoKSB7XHJcbiAgICB0aGlzLl9zdWJzY3JpYmVycyA9IDxhbnk+bnVsbDtcclxuICB9XHJcblxyXG4gIC8qKiBldmVudCBidWJibGluZyAtIGRvY3VtZW50IGxhdGVyLiAqL1xyXG5cclxuICAvLyBudWxsIG9yIHVuZGVmaW5lZCAnZ2V0UGFyZW50Rm4nIG1lYW5zIEV2ZW50IGRvZXMgbm90IG5lZWQgdG8gYnViYmxlIGkuZS4gdGhhdCBpdCBpcyBhbHdheXMgZW5hYmxlZCAtIC5cclxuICBzdGF0aWMgYnViYmxlRXZlbnQodGFyZ2V0OiBhbnksIGdldFBhcmVudEZuPzogKCgpID0+IGFueSkpIHtcclxuICAgIHRhcmdldC5fZ2V0RXZlbnRQYXJlbnQgPSBnZXRQYXJlbnRGbiB8fCBudWxsO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgRW5hYmxlcyBvciBkaXNhYmxlcyB0aGUgbmFtZWQgZXZlbnQgZm9yIGFuIG9iamVjdCBhbmQgYWxsIG9mIGl0cyBjaGlsZHJlbi5cclxuICA+ICAgICAgQnJlZXplRXZlbnQuZW5hYmxlKOKAnHByb3BlcnR5Q2hhbmdlZOKAnSwgbXlFbnRpdHlNYW5hZ2VyLCBmYWxzZSlcclxuXHJcbiAgd2lsbCBkaXNhYmxlIGFsbCBFbnRpdHlBc3BlY3QucHJvcGVydHlDaGFuZ2VkIGV2ZW50cyB3aXRoaW4gYSBFbnRpdHlNYW5hZ2VyLlxyXG4gID4gICAgICBCcmVlemVFdmVudC5lbmFibGUo4oCccHJvcGVydHlDaGFuZ2Vk4oCdLCBteUVudGl0eU1hbmFnZXIsIHRydWUpXHJcblxyXG4gIHdpbGwgZW5hYmxlIGFsbCBFbnRpdHlBc3BlY3QucHJvcGVydHlDaGFuZ2VkIGV2ZW50cyB3aXRoaW4gYSBFbnRpdHlNYW5hZ2VyLlxyXG4gID4gICAgICBCcmVlemVFdmVudC5lbmFibGUo4oCccHJvcGVydHlDaGFuZ2Vk4oCdLCBteUVudGl0eS5lbnRpdHlBc3BlY3QsIGZhbHNlKVxyXG5cclxuICB3aWxsIGRpc2FibGUgRW50aXR5QXNwZWN0LnByb3BlcnR5Q2hhbmdlZCBldmVudHMgZm9yIGEgc3BlY2lmaWMgZW50aXR5LlxyXG4gID4gICAgICBCcmVlemVFdmVudC5lbmFibGUo4oCccHJvcGVydHlDaGFuZ2Vk4oCdLCBteUVudGl0eS5lbnRpdHlBc3BlY3QsIG51bGwpXHJcblxyXG4gIHdpbGwgcmVtb3ZlcyBhbnkgZW5hYmxpbmcgLyBkaXNhYmxpbmcgYXQgdGhlIGVudGl0eSBhc3BlY3QgbGV2ZWwgc28gbm93IGFueSAnRXZlbnQuZW5hYmxlJyBjYWxscyBhdCB0aGUgRW50aXR5TWFuYWdlciBsZXZlbCxcclxuICBtYWRlIGVpdGhlciBwcmV2aW91c2x5IG9yIGluIHRoZSBmdXR1cmUsIHdpbGwgY29udHJvbCBub3RpZmljYXRpb24uXHJcbiAgPiAgICAgIEJyZWV6ZUV2ZW50LmVuYWJsZSjigJx2YWxpZGF0aW9uRXJyb3JzQ2hhbmdlZOKAnSwgbXlFbnRpdHlNYW5hZ2VyLCBmdW5jdGlvbihlbSkge1xyXG4gID4gICAgICAgICAgcmV0dXJuIGVtLmN1c3RvbVRhZyA9PT0g4oCcYmx1ZeKAnTtcclxuICA+ICAgICAgfSlcclxuXHJcblxyXG4gIHdpbGwgZWl0aGVyIGVuYWJsZSBvciBkaXNhYmxlIG15RW50aXR5TWFuYWdlciBiYXNlZCBvbiB0aGUgY3VycmVudCB2YWx1ZSBvZiBhIOKAmGN1c3RvbVRhZ+KAmSBwcm9wZXJ0eSBvbiBteUVudGl0eU1hbmFnZXIuXHJcbiAgTm90ZSB0aGF0IHRoaXMgaXMgZHluYW1pYywgY2hhbmdpbmcgdGhlIGN1c3RvbVRhZyB2YWx1ZSB3aWxsIGNhdXNlIGV2ZW50cyB0byBiZSBlbmFibGVkIG9yIGRpc2FibGVkIGltbWVkaWF0ZWx5LlxyXG4gIEBwYXJhbSBldmVudE5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgZXZlbnQuXHJcbiAgQHBhcmFtIHRhcmdldCAtIFRoZSBvYmplY3QgYXQgd2hpY2ggZW5hYmxpbmcgb3IgZGlzYWJsaW5nIHdpbGwgb2NjdXIuICBBbGwgZXZlbnQgbm90aWZpY2F0aW9ucyB0aGF0IG9jY3VyIHRvIHRoaXMgb2JqZWN0IG9yXHJcbiAgY2hpbGRyZW4gb2YgdGhpcyBvYmplY3Qgd2lsbCBiZSBlbmFibGVkIG9yIGRpc2FibGVkLlxyXG4gIEBwYXJhbSBpc0VuYWJsZWQgLSBBIGJvb2xlYW4sIGEgbnVsbCBvciBhIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBlaXRoZXIgYSBib29sZWFuIG9yIGEgbnVsbC5cclxuICAqKi9cclxuIHN0YXRpYyBlbmFibGUoZXZlbnROYW1lOiBzdHJpbmcsIG9iajogT2JqZWN0LCBpc0VuYWJsZWQ6IGJvb2xlYW4gfCAoKHg6IGFueSkgPT4gYm9vbGVhbikpIHtcclxuICAgIGFzc2VydFBhcmFtKGV2ZW50TmFtZSwgXCJldmVudE5hbWVcIikuaXNOb25FbXB0eVN0cmluZygpLmNoZWNrKCk7XHJcbiAgICBhc3NlcnRQYXJhbShvYmosIFwib2JqXCIpLmlzT2JqZWN0KCkuY2hlY2soKTtcclxuICAgIGFzc2VydFBhcmFtKGlzRW5hYmxlZCwgXCJpc0VuYWJsZWRcIikuaXNCb29sZWFuKCkuaXNPcHRpb25hbCgpLm9yKCkuaXNGdW5jdGlvbigpLmNoZWNrKCk7XHJcbiAgICBsZXQgb2IgPSA8YW55Pm9iajtcclxuICAgIGlmICghb2IuXyRldmVudE1hcCkge1xyXG4gICAgICBvYi5fJGV2ZW50TWFwID0ge307XHJcbiAgICB9XHJcbiAgICBvYi5fJGV2ZW50TWFwW2V2ZW50TmFtZV0gPSBpc0VuYWJsZWQ7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICBSZXR1cm5zIHdoZXRoZXIgZm9yIGEgc3BlY2lmaWMgZXZlbnQgYW5kIGEgc3BlY2lmaWMgb2JqZWN0IGFuZCBpdHMgY2hpbGRyZW4sIG5vdGlmaWNhdGlvbiBpcyBlbmFibGVkIG9yIGRpc2FibGVkIG9yIG5vdCBzZXQuXHJcbiAgPiAgICAgIEJyZWV6ZUV2ZW50LmlzRW5hYmxlZCjigJxwcm9wZXJ0eUNoYW5nZWTigJ0sIG15RW50aXR5TWFuYWdlcilcclxuICA+IFxyXG4gIEBwYXJhbSBldmVudE5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgZXZlbnQuXHJcbiAgQHBhcmFtIHRhcmdldCAtIFRoZSBvYmplY3QgZm9yIHdoaWNoIHdlIHdhbnQgdG8ga25vdyBpZiBub3RpZmljYXRpb25zIGFyZSBlbmFibGVkLlxyXG4gIEByZXR1cm4gQSBudWxsIGlzIHJldHVybmVkIGlmIHRoaXMgdmFsdWUgaGFzIG5vdCBiZWVuIHNldC5cclxuICAqKi9cclxuICBzdGF0aWMgaXNFbmFibGVkKGV2ZW50TmFtZTogc3RyaW5nLCBvYmo6IE9iamVjdCkge1xyXG4gICAgYXNzZXJ0UGFyYW0oZXZlbnROYW1lLCBcImV2ZW50TmFtZVwiKS5pc05vbkVtcHR5U3RyaW5nKCkuY2hlY2soKTtcclxuICAgIGFzc2VydFBhcmFtKG9iaiwgXCJvYmpcIikuaXNPYmplY3QoKS5jaGVjaygpO1xyXG4gICAgLy8gbnVsbCBpcyBvayAtIGl0IGp1c3QgbWVhbnMgdGhhdCB0aGUgb2JqZWN0IGlzIGF0IHRoZSB0b3AgbGV2ZWwuXHJcbiAgICBpZiAoKDxhbnk+b2JqKS5fZ2V0RXZlbnRQYXJlbnQgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJUaGlzIG9iamVjdCBkb2VzIG5vdCBzdXBwb3J0IGV2ZW50IGVuYWJsaW5nL2Rpc2FibGluZ1wiKTtcclxuICAgIH1cclxuICAgIC8vIHJldHVybiBjdG9yLl9pc0VuYWJsZWQoZ2V0RnVsbEV2ZW50TmFtZShldmVudE5hbWUpLCBvYmopO1xyXG4gICAgcmV0dXJuIEJyZWV6ZUV2ZW50Ll9pc0VuYWJsZWQoZXZlbnROYW1lLCAzKTtcclxuICB9XHJcblxyXG4gIC8qKiBAaGlkZGVuIEBpbnRlcm5hbCAqL1xyXG4gIHN0YXRpYyBfaXNFbmFibGVkID0gZnVuY3Rpb24gKGV2ZW50TmFtZTogc3RyaW5nLCBvYmo6IE9iamVjdCkge1xyXG4gICAgbGV0IGlzRW5hYmxlZDogYW55ID0gbnVsbDtcclxuICAgIGxldCBvYiA9IDxhbnk+b2JqO1xyXG4gICAgbGV0IGV2ZW50TWFwID0gb2IuXyRldmVudE1hcDtcclxuICAgIGlmIChldmVudE1hcCkge1xyXG4gICAgICBpc0VuYWJsZWQgPSBldmVudE1hcFtldmVudE5hbWVdO1xyXG4gICAgfVxyXG4gICAgaWYgKGlzRW5hYmxlZCAhPSBudWxsKSB7XHJcbiAgICAgIGlmICh0eXBlb2YgaXNFbmFibGVkID09PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgICAgcmV0dXJuICEhaXNFbmFibGVkKG9iaik7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgcmV0dXJuICEhaXNFbmFibGVkO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsZXQgcGFyZW50ID0gb2IuX2dldEV2ZW50UGFyZW50ICYmIG9iLl9nZXRFdmVudFBhcmVudCgpO1xyXG4gICAgICBpZiAocGFyZW50KSB7XHJcbiAgICAgICAgcmV0dXJuICEhdGhpcy5faXNFbmFibGVkKGV2ZW50TmFtZSwgcGFyZW50KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBkZWZhdWx0IGlmIG5vdCBleHBsaWNpdGx5IGRpc2FibGVkLlxyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcblxyXG59XHJcblxyXG4vLyBsZWdhY3kgc3VwcG9ydCAtIGRlbGliZXJhdGVseSBub3QgdHlwZWRcclxuKGNvcmUgYXMgYW55KS5FdmVudCA9IEJyZWV6ZUV2ZW50OyJdfQ==