Implement JQHTML function cache ID system and fix bundle compilation Implement underscore prefix for system tables Fix JS syntax linter to support decorators and grant exception to Task system SPA: Update planning docs and wishlists with remaining features SPA: Document Navigation API abandonment and future enhancements Implement SPA browser integration with History API (Phase 1) Convert contacts view page to SPA action Convert clients pages to SPA actions and document conversion procedure SPA: Merge GET parameters and update documentation Implement SPA route URL generation in JavaScript and PHP Implement SPA bootstrap controller architecture Add SPA routing manual page (rsx:man spa) Add SPA routing documentation to CLAUDE.md Phase 4 Complete: Client-side SPA routing implementation Update get_routes() consumers for unified route structure Complete SPA Phase 3: PHP-side route type detection and is_spa flag Restore unified routes structure and Manifest_Query class Refactor route indexing and add SPA infrastructure Phase 3 Complete: SPA route registration in manifest Implement SPA Phase 2: Extract router code and test decorators Rename Jqhtml_Component to Component and complete SPA foundation setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
191 lines
19 KiB
JavaScript
Executable File
191 lines
19 KiB
JavaScript
Executable File
"use strict";
|
|
|
|
// @FILE-SUBCLASS-01-EXCEPTION
|
|
|
|
/**
|
|
* Base class for JavaScript ORM models
|
|
*
|
|
* Provides core functionality for fetching records from backend PHP models.
|
|
* All model stubs generated by the manifest extend this base class.
|
|
*
|
|
* Example usage:
|
|
* // Fetch single record
|
|
* const user = await User_Model.fetch(123);
|
|
*
|
|
* // Fetch multiple records
|
|
* const users = await User_Model.fetch([1, 2, 3]);
|
|
*
|
|
* // Create instance with data
|
|
* const user = new User_Model({id: 1, name: 'John'});
|
|
*
|
|
* @Instantiatable
|
|
*/
|
|
class Rsx_Js_Model {
|
|
/**
|
|
* Constructor - Initialize model instance with data
|
|
*
|
|
* @param {Object} data - Key-value pairs to populate the model
|
|
*/
|
|
constructor() {
|
|
let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
// __MODEL SYSTEM: Enables automatic ORM instantiation when fetching from PHP models.
|
|
// PHP models add "__MODEL": "ClassName" to JSON, JavaScript uses it to create proper instances.
|
|
// This provides typed model objects instead of plain JSON, with methods and type checking.
|
|
|
|
// This constructor filters out the __MODEL marker that was used to identify which class
|
|
// to instantiate, keeping only the actual data properties on the instance.
|
|
const {
|
|
__MODEL,
|
|
...modelData
|
|
} = data;
|
|
Object.assign(this, modelData);
|
|
}
|
|
|
|
/**
|
|
* Fetch record(s) from the backend model
|
|
*
|
|
* This method mirrors the PHP Model::fetch() functionality.
|
|
* The backend model must have a fetch() method with the
|
|
* #[Ajax_Endpoint_Model_Fetch] annotation to be callable.
|
|
*
|
|
* @param {number|Array} id - Single ID or array of IDs to fetch
|
|
* @returns {Promise} - Single model instance, array of instances, or false
|
|
*/
|
|
static async fetch(id) {
|
|
const CurrentClass = this;
|
|
// Get the model class name from the current class
|
|
const modelName = CurrentClass.name;
|
|
const response = await $.ajax({
|
|
url: `/_fetch/${modelName}`,
|
|
method: 'POST',
|
|
data: {
|
|
id: id
|
|
},
|
|
dataType: 'json'
|
|
});
|
|
|
|
// Handle response based on type
|
|
if (response === false) {
|
|
return false;
|
|
}
|
|
|
|
// Use _instantiate_models_recursive to handle ORM instantiation
|
|
// This will automatically detect __MODEL properties and create appropriate instances
|
|
return Rsx_Js_Model._instantiate_models_recursive(response);
|
|
}
|
|
|
|
/**
|
|
* Get the model class name
|
|
* Used internally for API calls
|
|
*
|
|
* @returns {string} The class name
|
|
*/
|
|
static getModelName() {
|
|
const CurrentClass = this;
|
|
return CurrentClass.name;
|
|
}
|
|
|
|
/**
|
|
* Refresh this instance with latest data from server
|
|
*
|
|
* @returns {Promise} Updated instance or false if not found
|
|
*/
|
|
async refresh() {
|
|
const that = this;
|
|
if (!that.id) {
|
|
shouldnt_happen('Cannot refresh model without id property');
|
|
}
|
|
const fresh = await that.constructor.fetch(that.id);
|
|
if (fresh === false) {
|
|
return false;
|
|
}
|
|
|
|
// Update this instance with fresh data
|
|
Object.assign(that, fresh);
|
|
return that;
|
|
}
|
|
|
|
/**
|
|
* Convert model instance to plain object
|
|
* Useful for serialization or sending to APIs
|
|
*
|
|
* @returns {Object} Plain object representation
|
|
*/
|
|
toObject() {
|
|
const that = this;
|
|
const obj = {};
|
|
for (const key in that) {
|
|
if (that.hasOwnProperty(key) && typeof that[key] !== 'function') {
|
|
obj[key] = that[key];
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* Convert model instance to JSON string
|
|
*
|
|
* @returns {string} JSON representation
|
|
*/
|
|
toJSON() {
|
|
const that = this;
|
|
return JSON.stringify(that.toObject());
|
|
}
|
|
|
|
/**
|
|
* Recursively instantiate ORM models in response data
|
|
*
|
|
* Looks for objects with __MODEL property and instantiates the appropriate
|
|
* JavaScript model class if it exists in the global scope.
|
|
*
|
|
* @param {*} data - The data to process (can be any type)
|
|
* @returns {*} The data with ORM objects instantiated
|
|
*/
|
|
static _instantiate_models_recursive(data) {
|
|
// __MODEL SYSTEM: Enables automatic ORM instantiation when fetching from PHP models.
|
|
// PHP models add "__MODEL": "ClassName" to JSON, JavaScript uses it to create proper instances.
|
|
// This provides typed model objects instead of plain JSON, with methods and type checking.
|
|
|
|
// This recursive processor scans all API response data looking for __MODEL markers.
|
|
// When found, it attempts to instantiate the appropriate JavaScript model class,
|
|
// converting {__MODEL: "User_Model", id: 1, name: "John"} into new User_Model({...}).
|
|
// Works recursively through arrays and nested objects to handle complex data structures.
|
|
// Handle null/undefined
|
|
if (data === null || data === undefined) {
|
|
return data;
|
|
}
|
|
|
|
// Handle arrays - recursively process each element
|
|
if (Array.isArray(data)) {
|
|
return data.map(item => Rsx_Js_Model._instantiate_models_recursive(item));
|
|
}
|
|
|
|
// Handle objects
|
|
if (typeof data === 'object') {
|
|
// Check if this object has a __MODEL property
|
|
if (data.__MODEL && typeof data.__MODEL === 'string') {
|
|
// Try to find the model class in the global scope
|
|
const ModelClass = window[data.__MODEL];
|
|
|
|
// If the model class exists and extends Rsx_Js_Model, instantiate it
|
|
// Dynamic model resolution requires checking class existence - @JS-DEFENSIVE-01-EXCEPTION
|
|
if (ModelClass && ModelClass.prototype instanceof Rsx_Js_Model) {
|
|
return new ModelClass(data);
|
|
}
|
|
}
|
|
|
|
// Recursively process all object properties
|
|
const result = {};
|
|
for (const key in data) {
|
|
if (data.hasOwnProperty(key)) {
|
|
result[key] = Rsx_Js_Model._instantiate_models_recursive(data[key]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Return primitive values as-is
|
|
return data;
|
|
}
|
|
}
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSc3hfSnNfTW9kZWwiLCJjb25zdHJ1Y3RvciIsImRhdGEiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJ1bmRlZmluZWQiLCJfX01PREVMIiwibW9kZWxEYXRhIiwiT2JqZWN0IiwiYXNzaWduIiwiZmV0Y2giLCJpZCIsIkN1cnJlbnRDbGFzcyIsIm1vZGVsTmFtZSIsIm5hbWUiLCJyZXNwb25zZSIsIiQiLCJhamF4IiwidXJsIiwibWV0aG9kIiwiZGF0YVR5cGUiLCJfaW5zdGFudGlhdGVfbW9kZWxzX3JlY3Vyc2l2ZSIsImdldE1vZGVsTmFtZSIsInJlZnJlc2giLCJ0aGF0Iiwic2hvdWxkbnRfaGFwcGVuIiwiZnJlc2giLCJ0b09iamVjdCIsIm9iaiIsImtleSIsImhhc093blByb3BlcnR5IiwidG9KU09OIiwiSlNPTiIsInN0cmluZ2lmeSIsIkFycmF5IiwiaXNBcnJheSIsIm1hcCIsIml0ZW0iLCJNb2RlbENsYXNzIiwid2luZG93IiwicHJvdG90eXBlIiwicmVzdWx0Il0sInNvdXJjZXMiOlsiYXBwL1JTcGFkZS9Db3JlL0pzL1JzeF9Kc19Nb2RlbC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBARklMRS1TVUJDTEFTUy0wMS1FWENFUFRJT05cblxuLyoqXG4gKiBCYXNlIGNsYXNzIGZvciBKYXZhU2NyaXB0IE9STSBtb2RlbHNcbiAqXG4gKiBQcm92aWRlcyBjb3JlIGZ1bmN0aW9uYWxpdHkgZm9yIGZldGNoaW5nIHJlY29yZHMgZnJvbSBiYWNrZW5kIFBIUCBtb2RlbHMuXG4gKiBBbGwgbW9kZWwgc3R1YnMgZ2VuZXJhdGVkIGJ5IHRoZSBtYW5pZmVzdCBleHRlbmQgdGhpcyBiYXNlIGNsYXNzLlxuICpcbiAqIEV4YW1wbGUgdXNhZ2U6XG4gKiAgIC8vIEZldGNoIHNpbmdsZSByZWNvcmRcbiAqICAgY29uc3QgdXNlciA9IGF3YWl0IFVzZXJfTW9kZWwuZmV0Y2goMTIzKTtcbiAqXG4gKiAgIC8vIEZldGNoIG11bHRpcGxlIHJlY29yZHNcbiAqICAgY29uc3QgdXNlcnMgPSBhd2FpdCBVc2VyX01vZGVsLmZldGNoKFsxLCAyLCAzXSk7XG4gKlxuICogICAvLyBDcmVhdGUgaW5zdGFuY2Ugd2l0aCBkYXRhXG4gKiAgIGNvbnN0IHVzZXIgPSBuZXcgVXNlcl9Nb2RlbCh7aWQ6IDEsIG5hbWU6ICdKb2huJ30pO1xuICpcbiAqICBASW5zdGFudGlhdGFibGVcbiAqL1xuY2xhc3MgUnN4X0pzX01vZGVsIHtcbiAgICAvKipcbiAgICAgKiBDb25zdHJ1Y3RvciAtIEluaXRpYWxpemUgbW9kZWwgaW5zdGFuY2Ugd2l0aCBkYXRhXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gZGF0YSAtIEtleS12YWx1ZSBwYWlycyB0byBwb3B1bGF0ZSB0aGUgbW9kZWxcbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihkYXRhID0ge30pIHtcbiAgICAgICAgLy8gX19NT0RFTCBTWVNURU06IEVuYWJsZXMgYXV0b21hdGljIE9STSBpbnN0YW50aWF0aW9uIHdoZW4gZmV0Y2hpbmcgZnJvbSBQSFAgbW9kZWxzLlxuICAgICAgICAvLyBQSFAgbW9kZWxzIGFkZCBcIl9fTU9ERUxcIjogXCJDbGFzc05hbWVcIiB0byBKU09OLCBKYXZhU2NyaXB0IHVzZXMgaXQgdG8gY3JlYXRlIHByb3BlciBpbnN0YW5jZXMuXG4gICAgICAgIC8vIFRoaXMgcHJvdmlkZXMgdHlwZWQgbW9kZWwgb2JqZWN0cyBpbnN0ZWFkIG9mIHBsYWluIEpTT04sIHdpdGggbWV0aG9kcyBhbmQgdHlwZSBjaGVja2luZy5cblxuICAgICAgICAvLyBUaGlzIGNvbnN0cnVjdG9yIGZpbHRlcnMgb3V0IHRoZSBfX01PREVMIG1hcmtlciB0aGF0IHdhcyB1c2VkIHRvIGlkZW50aWZ5IHdoaWNoIGNsYXNzXG4gICAgICAgIC8vIHRvIGluc3RhbnRpYXRlLCBrZWVwaW5nIG9ubHkgdGhlIGFjdHVhbCBkYXRhIHByb3BlcnRpZXMgb24gdGhlIGluc3RhbmNlLlxuICAgICAgICBjb25zdCB7IF9fTU9ERUwsIC4uLm1vZGVsRGF0YSB9ID0gZGF0YTtcbiAgICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLCBtb2RlbERhdGEpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZldGNoIHJlY29yZChzKSBmcm9tIHRoZSBiYWNrZW5kIG1vZGVsXG4gICAgICpcbiAgICAgKiBUaGlzIG1ldGhvZCBtaXJyb3JzIHRoZSBQSFAgTW9kZWw6OmZldGNoKCkgZnVuY3Rpb25hbGl0eS5cbiAgICAgKiBUaGUgYmFja2VuZCBtb2RlbCBtdXN0IGhhdmUgYSBmZXRjaCgpIG1ldGhvZCB3aXRoIHRoZVxuICAgICAqICNbQWpheF9FbmRwb2ludF9Nb2RlbF9GZXRjaF0gYW5ub3RhdGlvbiB0byBiZSBjYWxsYWJsZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfEFycmF5fSBpZCAtIFNpbmdsZSBJRCBvciBhcnJheSBvZiBJRHMgdG8gZmV0Y2hcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gLSBTaW5nbGUgbW9kZWwgaW5zdGFuY2UsIGFycmF5IG9mIGluc3RhbmNlcywgb3IgZmFsc2VcbiAgICAgKi9cbiAgICBzdGF0aWMgYXN5bmMgZmV0Y2goaWQpIHtcbiAgICAgICAgY29uc3QgQ3VycmVudENsYXNzID0gdGhpcztcbiAgICAgICAgLy8gR2V0IHRoZSBtb2RlbCBjbGFzcyBuYW1lIGZyb20gdGhlIGN1cnJlbnQgY2xhc3NcbiAgICAgICAgY29uc3QgbW9kZWxOYW1lID0gQ3VycmVudENsYXNzLm5hbWU7XG5cbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCAkLmFqYXgoe1xuICAgICAgICAgICAgdXJsOiBgL19mZXRjaC8ke21vZGVsTmFtZX1gLFxuICAgICAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgICAgICBkYXRhOiB7IGlkOiBpZCB9LFxuICAgICAgICAgICAgZGF0YVR5cGU6ICdqc29uJyxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gSGFuZGxlIHJlc3BvbnNlIGJhc2VkIG9uIHR5cGVcbiAgICAgICAgaWYgKHJlc3BvbnNlID09PSBmYWxzZSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVXNlIF9pbnN0YW50aWF0ZV9tb2RlbHNfcmVjdXJzaXZlIHRvIGhhbmRsZSBPUk0gaW5zdGFudGlhdGlvblxuICAgICAgICAvLyBUaGlzIHdpbGwgYXV0b21hdGljYWxseSBkZXRlY3QgX19NT0RFTCBwcm9wZXJ0aWVzIGFuZCBjcmVhdGUgYXBwcm9wcmlhdGUgaW5zdGFuY2VzXG4gICAgICAgIHJldHVybiBSc3hfSnNfTW9kZWwuX2luc3RhbnRpYXRlX21vZGVsc19yZWN1cnNpdmUocmVzcG9uc2UpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgbW9kZWwgY2xhc3MgbmFtZVxuICAgICAqIFVzZWQgaW50ZXJuYWxseSBmb3IgQVBJIGNhbGxzXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2xhc3MgbmFtZVxuICAgICAqL1xuICAgIHN0YXRpYyBnZXRNb2RlbE5hbWUoKSB7XG4gICAgICAgIGNvbnN0IEN1cnJlbnRDbGFzcyA9IHRoaXM7XG4gICAgICAgIHJldHVybiBDdXJyZW50Q2xhc3MubmFtZTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWZyZXNoIHRoaXMgaW5zdGFuY2Ugd2l0aCBsYXRlc3QgZGF0YSBmcm9tIHNlcnZlclxuICAgICAqXG4gICAgICogQHJldHVybnMge1Byb21pc2V9IFVwZGF0ZWQgaW5zdGFuY2Ugb3IgZmFsc2UgaWYgbm90IGZvdW5kXG4gICAgICovXG4gICAgYXN5bmMgcmVmcmVzaCgpIHtcbiAgICAgICAgY29uc3QgdGhhdCA9IHRoaXM7XG4gICAgICAgIGlmICghdGhhdC5pZCkge1xuICAgICAgICAgICAgc2hvdWxkbnRfaGFwcGVuKCdDYW5ub3QgcmVmcmVzaCBtb2RlbCB3aXRob3V0IGlkIHByb3BlcnR5Jyk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBmcmVzaCA9IGF3YWl0IHRoYXQuY29uc3RydWN0b3IuZmV0Y2godGhhdC5pZCk7XG5cbiAgICAgICAgaWYgKGZyZXNoID09PSBmYWxzZSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVXBkYXRlIHRoaXMgaW5zdGFuY2Ugd2l0aCBmcmVzaCBkYXRhXG4gICAgICAgIE9iamVjdC5hc3NpZ24odGhhdCwgZnJlc2gpO1xuICAgICAgICByZXR1cm4gdGhhdDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb252ZXJ0IG1vZGVsIGluc3RhbmNlIHRvIHBsYWluIG9iamVjdFxuICAgICAqIFVzZWZ1bCBmb3Igc2VyaWFsaXphdGlvbiBvciBzZW5kaW5nIHRvIEFQSXNcbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHtPYmplY3R9IFBsYWluIG9iamVjdCByZXByZXNlbnRhdGlvblxuICAgICAqL1xuICAgIHRvT2JqZWN0KCkge1xuICAgICAgICBjb25zdCB0aGF0ID0gdGhpcztcbiAgICAgICAgY29uc3Qgb2JqID0ge307XG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIHRoYXQpIHtcbiAgICAgICAgICAgIGlmICh0aGF0Lmhhc093blByb3BlcnR5KGtleSkgJiYgdHlwZW9mIHRoYXRba2V5XSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgIG9ialtrZXldID0gdGhhdFtrZXldO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBvYmo7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29udmVydCBtb2RlbCBpbnN0YW5jZSB0byBKU09OIHN0cmluZ1xuICAgICAqXG4gICAgICogQHJldHVybnMge3N0cmluZ30gSlNPTiByZXByZXNlbnRhdGlvblxuICAgICAqL1xuICAgIHRvSlNPTigpIHtcbiAgICAgICAgY29uc3QgdGhhdCA9IHRoaXM7XG4gICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh0aGF0LnRvT2JqZWN0KCkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlY3Vyc2l2ZWx5IGluc3RhbnRpYXRlIE9STSBtb2RlbHMgaW4gcmVzcG9uc2UgZGF0YVxuICAgICAqXG4gICAgICogTG9va3MgZm9yIG9iamVjdHMgd2l0aCBfX01PREVMIHByb3BlcnR5IGFuZCBpbnN0YW50aWF0ZXMgdGhlIGFwcHJvcHJpYXRlXG4gICAgICogSmF2YVNjcmlwdCBtb2RlbCBjbGFzcyBpZiBpdCBleGlzdHMgaW4gdGhlIGdsb2JhbCBzY29wZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7Kn0gZGF0YSAtIFRoZSBkYXRhIHRvIHByb2Nlc3MgKGNhbiBiZSBhbnkgdHlwZSlcbiAgICAgKiBAcmV0dXJucyB7Kn0gVGhlIGRhdGEgd2l0aCBPUk0gb2JqZWN0cyBpbnN0YW50aWF0ZWRcbiAgICAgKi9cbiAgICBzdGF0aWMgX2luc3RhbnRpYXRlX21vZGVsc19yZWN1cnNpdmUoZGF0YSkge1xuICAgICAgICAvLyBfX01PREVMIFNZU1RFTTogRW5hYmxlcyBhdXRvbWF0aWMgT1JNIGluc3RhbnRpYXRpb24gd2hlbiBmZXRjaGluZyBmcm9tIFBIUCBtb2RlbHMuXG4gICAgICAgIC8vIFBIUCBtb2RlbHMgYWRkIFwiX19NT0RFTFwiOiBcIkNsYXNzTmFtZVwiIHRvIEpTT04sIEphdmFTY3JpcHQgdXNlcyBpdCB0byBjcmVhdGUgcHJvcGVyIGluc3RhbmNlcy5cbiAgICAgICAgLy8gVGhpcyBwcm92aWRlcyB0eXBlZCBtb2RlbCBvYmplY3RzIGluc3RlYWQgb2YgcGxhaW4gSlNPTiwgd2l0aCBtZXRob2RzIGFuZCB0eXBlIGNoZWNraW5nLlxuXG4gICAgICAgIC8vIFRoaXMgcmVjdXJzaXZlIHByb2Nlc3NvciBzY2FucyBhbGwgQVBJIHJlc3BvbnNlIGRhdGEgbG9va2luZyBmb3IgX19NT0RFTCBtYXJrZXJzLlxuICAgICAgICAvLyBXaGVuIGZvdW5kLCBpdCBhdHRlbXB0cyB0byBpbnN0YW50aWF0ZSB0aGUgYXBwcm9wcmlhdGUgSmF2YVNjcmlwdCBtb2RlbCBjbGFzcyxcbiAgICAgICAgLy8gY29udmVydGluZyB7X19NT0RFTDogXCJVc2VyX01vZGVsXCIsIGlkOiAxLCBuYW1lOiBcIkpvaG5cIn0gaW50byBuZXcgVXNlcl9Nb2RlbCh7Li4ufSkuXG4gICAgICAgIC8vIFdvcmtzIHJlY3Vyc2l2ZWx5IHRocm91Z2ggYXJyYXlzIGFuZCBuZXN0ZWQgb2JqZWN0cyB0byBoYW5kbGUgY29tcGxleCBkYXRhIHN0cnVjdHVyZXMuXG4gICAgICAgIC8vIEhhbmRsZSBudWxsL3VuZGVmaW5lZFxuICAgICAgICBpZiAoZGF0YSA9PT0gbnVsbCB8fCBkYXRhID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSGFuZGxlIGFycmF5cyAtIHJlY3Vyc2l2ZWx5IHByb2Nlc3MgZWFjaCBlbGVtZW50XG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KGRhdGEpKSB7XG4gICAgICAgICAgICByZXR1cm4gZGF0YS5tYXAoKGl0ZW0pID0+IFJzeF9Kc19Nb2RlbC5faW5zdGFudGlhdGVfbW9kZWxzX3JlY3Vyc2l2ZShpdGVtKSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBIYW5kbGUgb2JqZWN0c1xuICAgICAgICBpZiAodHlwZW9mIGRhdGEgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAvLyBDaGVjayBpZiB0aGlzIG9iamVjdCBoYXMgYSBfX01PREVMIHByb3BlcnR5XG4gICAgICAgICAgICBpZiAoZGF0YS5fX01PREVMICYmIHR5cGVvZiBkYXRhLl9fTU9ERUwgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgLy8gVHJ5IHRvIGZpbmQgdGhlIG1vZGVsIGNsYXNzIGluIHRoZSBnbG9iYWwgc2NvcGVcbiAgICAgICAgICAgICAgICBjb25zdCBNb2RlbENsYXNzID0gd2luZG93W2RhdGEuX19NT0RFTF07XG5cbiAgICAgICAgICAgICAgICAvLyBJZiB0aGUgbW9kZWwgY2xhc3MgZXhpc3RzIGFuZCBleHRlbmRzIFJzeF9Kc19Nb2RlbCwgaW5zdGFudGlhdGUgaXRcbiAgICAgICAgICAgICAgICAvLyBEeW5hbWljIG1vZGVsIHJlc29sdXRpb24gcmVxdWlyZXMgY2hlY2tpbmcgY2xhc3MgZXhpc3RlbmNlIC0gQEpTLURFRkVOU0lWRS0wMS1FWENFUFRJT05cbiAgICAgICAgICAgICAgICBpZiAoTW9kZWxDbGFzcyAmJiBNb2RlbENsYXNzLnByb3RvdHlwZSBpbnN0YW5jZW9mIFJzeF9Kc19Nb2RlbCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbmV3IE1vZGVsQ2xhc3MoZGF0YSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBSZWN1cnNpdmVseSBwcm9jZXNzIGFsbCBvYmplY3QgcHJvcGVydGllc1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0ge307XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGtleSBpbiBkYXRhKSB7XG4gICAgICAgICAgICAgICAgaWYgKGRhdGEuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICAgICAgICAgICAgICByZXN1bHRba2V5XSA9IFJzeF9Kc19Nb2RlbC5faW5zdGFudGlhdGVfbW9kZWxzX3JlY3Vyc2l2ZShkYXRhW2tleV0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBSZXR1cm4gcHJpbWl0aXZlIHZhbHVlcyBhcy1pc1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7O0FBQUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTUEsWUFBWSxDQUFDO0VBQ2Y7QUFDSjtB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
|