UNPKG

@magic-xpa/utils

Version:

magic utils package

734 lines • 65.5 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ import { NString, List, ApplicationException, StringBuilder, NChar, NNumber } from "@magic-xpa/mscorelib"; import { XMLConstants } from "../XMLConstants"; /// <summary> a helper class for the parsing of the XML</summary> var XmlParser = /** @class */ (function () { /// <summary> /// /// </summary> /// <param name="data"></param> function XmlParser(data) { if (data === void 0) { data = NString.Empty; } this._currIndex = 0; this._xmLdata = ""; this._history = new List(); // In order to allow recursive parsing we save prev data this.setXMLdata(data); this.setCurrIndex(0); } /// <summary> parse a string according to a set of delimiters and return the result in a vector</summary> /// <param name="str">the String which need be parted </param> /// <param name="delimiter">the delimiter which part different parts of str </param> /// <param name="isMagicXML">is needed tokenizer working on Magic XML, so the "=" sign will be delited in the end of every first token </param> /// <returns> tmpVector dynamically array, which consist tokens in every element, every token is String </returns> /// <summary> parse a string according to a set of delimiters and return the result in a vector</summary> /// <param name="str">the String which need be parted </param> /// <param name="delimiter">the delimiter which part different parts of str </param> /// <param name="isMagicXML">is needed tokenizer working on Magic XML, so the "=" sign will be delited in the end of every first token </param> /// <returns> tmpVector dynamically array, which consist tokens in every element, every token is String </returns> /** * @param {?} str * @param {?} delimiter * @param {?=} isMagicXML * @return {?} */ XmlParser.getTokens = /// <summary> parse a string according to a set of delimiters and return the result in a vector</summary> /// <param name="str">the String which need be parted </param> /// <param name="delimiter">the delimiter which part different parts of str </param> /// <param name="isMagicXML">is needed tokenizer working on Magic XML, so the "=" sign will be delited in the end of every first token </param> /// <returns> tmpVector dynamically array, which consist tokens in every element, every token is String </returns> /** * @param {?} str * @param {?} delimiter * @param {?=} isMagicXML * @return {?} */ function (str, delimiter, isMagicXML) { if (isMagicXML === void 0) { isMagicXML = true; } /** @type {?} */ var tokensVec = new List(); /** @type {?} */ var token = null; if (isMagicXML) { str = str.trim(); } /** @type {?} */ var strTok = str.split(delimiter.charAt(0)); for (var i = 0; i < strTok.length; i = i + 1) { // Split in C# creates a last empty string token if the source string ends with // the delimiter or if the string is empty (as opposed to Java that will ignore it) // therefore we have to break this loop if such case occurs. if (isMagicXML && i === strTok.length - 1 && strTok.length % 2 === 1) { break; } token = strTok[i]; if (isMagicXML) { // the 1st token in the pair comes with "=", remove it. if (i % 2 === 0) { token = token.trim(); if (token.endsWith("=")) { token = token.substr(0, token.length - 1); } } else if (token === "") token = " "; } if (token === null) throw new ApplicationException("in ClientManager.Instance.XMLParser.getTokens() null token value"); tokensVec.push(token); } return tokensVec; }; /// <summary>unscape from: /// {"&amp;",\\, \q, \o, \l, \g, \e, \\r, \\n}, to: /// {"&", \, ", ', <, >, =, \r, \n} /// <param name="str">String to be converted</param> /// <returns>unescaped string</returns> /// <summary>unscape from: /// {"&amp;",\\, \q, \o, \l, \g, \e, \\r, \\n}, to: /// {"&", \, ", ', <, >, =, \r, \n} /// <param name="str">String to be converted</param> /// <returns>unescaped string</returns> /** * @param {?} str * @return {?} */ XmlParser.unescape = /// <summary>unscape from: /// {"&amp;",\\, \q, \o, \l, \g, \e, \\r, \\n}, to: /// {"&", \, ", ', <, >, =, \r, \n} /// <param name="str">String to be converted</param> /// <returns>unescaped string</returns> /** * @param {?} str * @return {?} */ function (str) { /** @type {?} */ var unescapedString = new StringBuilder(str.length); for (var i = 0; i < str.length; i++) { if (str[i] !== '\\') { unescapedString.Append(str[i]); continue; } switch (str[++i]) { case 'q': unescapedString.Append('\"'); break; case 'o': unescapedString.Append('\''); break; case 'l': unescapedString.Append('<'); break; case 'g': unescapedString.Append('>'); break; case 'e': unescapedString.Append('='); break; case 'r': unescapedString.Append('\r'); break; case 'n': unescapedString.Append('\n'); break; default: unescapedString.Append(str[i]); break; } } return (unescapedString.ToString()); }; /// <summary>escape from: /// {\, ", ', <, >, =, \r, \n}, to: /// {\\, \q, \0, \l, \g, \e, \\r, \\n} /// <param name="str">String to be converted</param> /// <returns>escaped string</returns> /// <summary>escape from: /// {\, ", ', <, >, =, \r, \n}, to: /// {\\, \q, \0, \l, \g, \e, \\r, \\n} /// <param name="str">String to be converted</param> /// <returns>escaped string</returns> /** * @param {?} str * @return {?} */ XmlParser.escape = /// <summary>escape from: /// {\, ", ', <, >, =, \r, \n}, to: /// {\\, \q, \0, \l, \g, \e, \\r, \\n} /// <param name="str">String to be converted</param> /// <returns>escaped string</returns> /** * @param {?} str * @return {?} */ function (str) { /** @type {?} */ var escapedString = new StringBuilder(str.length * 2); for (var i = 0; i < str.length; i++) { switch (str[i]) { case '\\': escapedString.Append("\\\\"); break; case '"': escapedString.Append("\\q"); break; case '\'': escapedString.Append("\\o"); break; case '<': escapedString.Append("\\l"); break; case '>': escapedString.Append("\\g"); break; case '=': escapedString.Append("\\e"); break; case '\r': escapedString.Append("\r"); break; case '\n': escapedString.Append("\n"); break; default: escapedString.Append(str[i]); break; } } return (escapedString.ToString()); }; /// <summary> /// here we only need to take care of "&" so that Sax parser will be able to handle url /// </summary> /// <param name="str"></param> /// <returns></returns> /// <summary> /// here we only need to take care of "&" so that Sax parser will be able to handle url /// </summary> /// <param name="str"></param> /// <returns></returns> /** * @param {?} str * @return {?} */ XmlParser.escapeUrl = /// <summary> /// here we only need to take care of "&" so that Sax parser will be able to handle url /// </summary> /// <param name="str"></param> /// <returns></returns> /** * @param {?} str * @return {?} */ function (str) { return NString.Replace(str, "&", "&amp;"); }; /// <summary>get next tag name from current index in XML string</summary> /// <returns> next tag name </returns> /// <summary>get next tag name from current index in XML string</summary> /// <returns> next tag name </returns> /** * @return {?} */ XmlParser.prototype.getNextTag = /// <summary>get next tag name from current index in XML string</summary> /// <returns> next tag name </returns> /** * @return {?} */ function () { if (this._xmLdata.length - this._currIndex <= 1) { return null; // end of XML string } for (var tmpIndx = this._currIndex + 1; tmpIndx < this._xmLdata.length; tmpIndx++) { /** @type {?} */ var tmpChar = this._xmLdata[tmpIndx]; // a letter starts an element and ends with " ". "/" starts an element closing and ends with '>'. if (NChar.IsLetter(tmpChar) || tmpChar === '/') { /** @type {?} */ var endOfTag = NString.IndexOfAny(this._xmLdata, XmlParser.endOfNameChar, tmpIndx, this._xmLdata.length - tmpIndx); if (endOfTag === -1) return null; else return this._xmLdata.substr(tmpIndx, endOfTag - tmpIndx); } } return null; }; /// <summary>Substring of XMLstring</summary> /// <returns> substring of XML string -from currIndex to endContext </returns> /// <summary>Substring of XMLstring</summary> /// <returns> substring of XML string -from currIndex to endContext </returns> /** * @param {?} endContext * @return {?} */ XmlParser.prototype.getXMLsubstring = /// <summary>Substring of XMLstring</summary> /// <returns> substring of XML string -from currIndex to endContext </returns> /** * @param {?} endContext * @return {?} */ function (endContext) { return this._xmLdata.substr(this._currIndex, endContext - this._currIndex); }; /// <summary>get current element value</summary> /// <returns> element's value </returns> /// <summary>get current element value</summary> /// <returns> element's value </returns> /** * @return {?} */ XmlParser.prototype.GetCurrentElementValue = /// <summary>get current element value</summary> /// <returns> element's value </returns> /** * @return {?} */ function () { this.setCurrIndex2EndOfTag(); /** @type {?} */ var endContext = this.getXMLdata().indexOf(XMLConstants.TAG_OPEN, this.getCurrIndex()); // read value of xml element /** @type {?} */ var value = this.getXMLsubstring(endContext); this.setCurrIndex2EndOfTag(); return value; }; /// <summary>set current index (on parsing time) to the end of current tag</summary> /// <summary>set current index (on parsing time) to the end of current tag</summary> /** * @return {?} */ XmlParser.prototype.setCurrIndex2EndOfTag = /// <summary>set current index (on parsing time) to the end of current tag</summary> /** * @return {?} */ function () { this._currIndex = this._xmLdata.indexOf(XMLConstants.TAG_CLOSE, this._currIndex) + 1; }; /// <summary>get int from string at parsing time</summary> /// <summary>get int from string at parsing time</summary> /** * @param {?} valueStr * @return {?} */ XmlParser.getInt = /// <summary>get int from string at parsing time</summary> /** * @param {?} valueStr * @return {?} */ function (valueStr) { return NNumber.Parse(valueStr.trim()); }; /// <summary>get boolean from string at parsing time</summary> /// <summary>get boolean from string at parsing time</summary> /** * @param {?} valueStr * @return {?} */ XmlParser.getBoolean = /// <summary>get boolean from string at parsing time</summary> /** * @param {?} valueStr * @return {?} */ function (valueStr) { return valueStr[0] === '1'; }; /// <summary>get/set functions 4 XMLstring & currIndex, for parser</summary> /// <summary>get/set functions 4 XMLstring & currIndex, for parser</summary> /** * @return {?} */ XmlParser.prototype.getCurrIndex = /// <summary>get/set functions 4 XMLstring & currIndex, for parser</summary> /** * @return {?} */ function () { return this._currIndex; }; /** * @return {?} */ XmlParser.prototype.getXMLdata = /** * @return {?} */ function () { return this._xmLdata; }; /** * @param {?} add * @return {?} */ XmlParser.prototype.add2CurrIndex = /** * @param {?} add * @return {?} */ function (add) { this._currIndex = this._currIndex + add; }; /** * @param {?} index * @return {?} */ XmlParser.prototype.setCurrIndex = /** * @param {?} index * @return {?} */ function (index) { this._currIndex = index; }; /** * @param {?} data * @return {?} */ XmlParser.prototype.setXMLdata = /** * @param {?} data * @return {?} */ function (data) { if (data !== null) this._xmLdata = data.trim(); else { this._xmLdata = null; this.setCurrIndex(0); } }; /// <summary> /// prepare the parser to read from the newXmlString /// </summary> /// <param name="newXmlString"></param> /// <summary> /// prepare the parser to read from the newXmlString /// </summary> /// <param name="newXmlString"></param> /** * @param {?} newXmlString * @return {?} */ XmlParser.prototype.PrepareFormReadString = /// <summary> /// prepare the parser to read from the newXmlString /// </summary> /// <param name="newXmlString"></param> /** * @param {?} newXmlString * @return {?} */ function (newXmlString) { this.setXMLdata(newXmlString); this.setCurrIndex(0); }; /// <summary> push the current parsing information into the history stack</summary> /// <summary> push the current parsing information into the history stack</summary> /** * @return {?} */ XmlParser.prototype.push = /// <summary> push the current parsing information into the history stack</summary> /** * @return {?} */ function () { this._history.push(this._currIndex); this._history.push(this._xmLdata); }; /// <summary> restore the previous parsing information from the history stack</summary> /// <summary> restore the previous parsing information from the history stack</summary> /** * @return {?} */ XmlParser.prototype.pop = /// <summary> restore the previous parsing information from the history stack</summary> /** * @return {?} */ function () { /** @type {?} */ var count = this._history.length; this._xmLdata = (/** @type {?} */ (this._history.get_Item(count - 1))); this._currIndex = (/** @type {?} */ (this._history.get_Item(count - 2))); this._history.SetSize(count - 2); }; /// <summary>gets a table cache xml and set the xmlparser data and index accordingly</summary> /// <summary>gets a table cache xml and set the xmlparser data and index accordingly</summary> /** * @param {?} data * @return {?} */ XmlParser.prototype.loadTableCacheData = /// <summary>gets a table cache xml and set the xmlparser data and index accordingly</summary> /** * @param {?} data * @return {?} */ function (data) { this.setXMLdata(data); this.setCurrIndex(0); }; /// <summary> /// Reads the XML from the element at the current position until the end of /// the element, returning the contents as a string. This allows deferring the /// processing of an element until the time is right to do so.<br/> /// The returned string contains the element tag itself. For example:<br/> /// - Assuming that the current element is 'element1', with 2 'innerElement' elements, the /// resulting string will look like this:<br/> /// <element1> /// <innerelement/> /// <innerelement/> /// </element1> /// /// This makes the result valid for processing by this XML parser. /// </summary> /// <returns></returns> /// <summary> /// Reads the XML from the element at the current position until the end of /// the element, returning the contents as a string. This allows deferring the /// processing of an element until the time is right to do so.<br/> /// The returned string contains the element tag itself. For example:<br/> /// - Assuming that the current element is 'element1', with 2 'innerElement' elements, the /// resulting string will look like this:<br/> /// <element1> /// <innerelement/> /// <innerelement/> /// </element1> /// /// This makes the result valid for processing by this XML parser. /// </summary> /// <returns></returns> /** * @return {?} */ XmlParser.prototype.ReadToEndOfCurrentElement = /// <summary> /// Reads the XML from the element at the current position until the end of /// the element, returning the contents as a string. This allows deferring the /// processing of an element until the time is right to do so.<br/> /// The returned string contains the element tag itself. For example:<br/> /// - Assuming that the current element is 'element1', with 2 'innerElement' elements, the /// resulting string will look like this:<br/> /// <element1> /// <innerelement/> /// <innerelement/> /// </element1> /// /// This makes the result valid for processing by this XML parser. /// </summary> /// <returns></returns> /** * @return {?} */ function () { // Get the current tag according to the value of _currIndex. /** @type {?} */ var currentTag = this.getNextTag(); /** @type {?} */ var currentTagIndex = this._xmLdata.indexOf(XMLConstants.TAG_OPEN + currentTag, this.getCurrIndex()); // Find the end of the element's block in the XML. // find next open tag /** @type {?} */ var nextOpenTagIndex = this._xmLdata.indexOf(XMLConstants.TAG_OPEN, currentTagIndex + 1); if (nextOpenTagIndex === -1) nextOpenTagIndex = this._xmLdata.length; // find a close tag BEFORE the next open tag /** @type {?} */ var elementEndIndex = NString.IndexOf(this._xmLdata, XMLConstants.TAG_TERM, this.getCurrIndex(), nextOpenTagIndex - this.getCurrIndex()); if (elementEndIndex === -1) // close tag was not found in range - we have inner elements, look for the full close tag elementEndIndex = this._xmLdata.indexOf("/" + currentTag, this.getCurrIndex()) + currentTag.length + XMLConstants.TAG_TERM.length; else elementEndIndex = elementEndIndex + XMLConstants.TAG_TERM.length; // Copy the element data so it can be returned. /** @type {?} */ var elementBlock = this.getXMLsubstring(elementEndIndex); // Move the parser to the end of the element block. this.setCurrIndex(elementEndIndex); return elementBlock; }; /** * @return {?} */ XmlParser.prototype.ReadContentOfCurrentElement = /** * @return {?} */ function () { // Get the current tag according to the value of _currIndex. /** @type {?} */ var currentTag = this.getNextTag(); // Find the end of the element's block in the XML. /** @type {?} */ var elementEndIndex = this._xmLdata.indexOf("</" + currentTag + ">", this.getCurrIndex()); if (elementEndIndex === -1) // Can't find the end of the current element - either XML is faulty or the element is empty. return NString.Empty; // Move to the end of the opening tag this.setCurrIndex2EndOfTag(); // Copy the content of the element (from the end of the opening tag to the beginning of the closing tag). /** @type {?} */ var elementBlock = this.getXMLsubstring(elementEndIndex); // Move the parser to the end of the element block. this.setCurrIndex(elementEndIndex); this.setCurrIndex2EndOfTag(); return elementBlock; }; /** * @param {?=} headCharCount * @param {?=} tailCharCount * @return {?} */ XmlParser.prototype.toString = /** * @param {?=} headCharCount * @param {?=} tailCharCount * @return {?} */ function (headCharCount, tailCharCount) { if (arguments.length === 0) { return this.ToString_0(); } return this.ToString_1(headCharCount, tailCharCount); }; /// <summary> /// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/> /// The method will show the XML data, trimming it to 20 characters before the /// current position (_currIndex) and up to 50 characters after the current position. /// The current position itself will be marked with a marker that looks like: /// |-{current index}-| <br/> /// The marker will be placed immediately before _xmlData[_currIndex]. /// </summary> /// <returns></returns> /// <summary> /// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/> /// The method will show the XML data, trimming it to 20 characters before the /// current position (_currIndex) and up to 50 characters after the current position. /// The current position itself will be marked with a marker that looks like: /// |-{current index}-| <br/> /// The marker will be placed immediately before _xmlData[_currIndex]. /// </summary> /// <returns></returns> /** * @return {?} */ XmlParser.prototype.ToString_0 = /// <summary> /// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/> /// The method will show the XML data, trimming it to 20 characters before the /// current position (_currIndex) and up to 50 characters after the current position. /// The current position itself will be marked with a marker that looks like: /// |-{current index}-| <br/> /// The marker will be placed immediately before _xmlData[_currIndex]. /// </summary> /// <returns></returns> /** * @return {?} */ function () { return this.toString(20, 50); }; /// <summary> /// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/> /// The method will show the XML data, trimming it to headCharCount characters before the /// current position (_currIndex) and up to tailCharCount characters after the current position. /// The current position itself will be marked with a marker that looks like: /// |-{current index}-| <br/> /// The marker will be placed immediately before _xmlData[_currIndex]. /// </summary> /// <param name="headCharCount">Number of characters to show before the current position marker.</param> /// <param name="tailCharCount">Number of characters to show after the current position marker.</param> /// <returns></returns> /// <summary> /// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/> /// The method will show the XML data, trimming it to headCharCount characters before the /// current position (_currIndex) and up to tailCharCount characters after the current position. /// The current position itself will be marked with a marker that looks like: /// |-{current index}-| <br/> /// The marker will be placed immediately before _xmlData[_currIndex]. /// </summary> /// <param name="headCharCount">Number of characters to show before the current position marker.</param> /// <param name="tailCharCount">Number of characters to show after the current position marker.</param> /// <returns></returns> /** * @param {?} headCharCount * @param {?} tailCharCount * @return {?} */ XmlParser.prototype.ToString_1 = /// <summary> /// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/> /// The method will show the XML data, trimming it to headCharCount characters before the /// current position (_currIndex) and up to tailCharCount characters after the current position. /// The current position itself will be marked with a marker that looks like: /// |-{current index}-| <br/> /// The marker will be placed immediately before _xmlData[_currIndex]. /// </summary> /// <param name="headCharCount">Number of characters to show before the current position marker.</param> /// <param name="tailCharCount">Number of characters to show after the current position marker.</param> /// <returns></returns> /** * @param {?} headCharCount * @param {?} tailCharCount * @return {?} */ function (headCharCount, tailCharCount) { /** @type {?} */ var markerPosition = Math.min(this._currIndex, this._xmLdata.length); /** @type {?} */ var segmentStartIndex = Math.max(0, markerPosition - headCharCount); /** @type {?} */ var segmentEndIndex = Math.min(this._xmLdata.length, markerPosition + tailCharCount); /** @type {?} */ var headLength = markerPosition - segmentStartIndex; /** @type {?} */ var tailLength = segmentEndIndex - markerPosition; /** @type {?} */ var segment = new StringBuilder(); if (segmentStartIndex > 0) segment.Append("..."); if (headLength > 0) segment.Append(this._xmLdata, segmentStartIndex, headLength); segment.Append("|-{").Append(this._currIndex).Append("}-|"); if (tailLength > 0) segment.Append(this._xmLdata, this._currIndex, tailLength); if (segmentEndIndex < this._xmLdata.length) segment.Append("..."); return segment.ToString(); }; /** * @return {?} */ XmlParser.prototype.SkipXMLElement = /** * @return {?} */ function () { /** @type {?} */ var endContext = this.getXMLdata().indexOf(XMLConstants.TAG_TERM, this.getCurrIndex()); if (endContext !== -1 && endContext < this.getXMLdata().length) { this.setCurrIndex2EndOfTag(); } }; XmlParser.endOfNameChar = [' ', '>']; return XmlParser; }()); export { XmlParser }; if (false) { /** @type {?} */ XmlParser.endOfNameChar; /** @type {?} */ XmlParser.prototype._currIndex; /** @type {?} */ XmlParser.prototype._xmLdata; /** @type {?} */ XmlParser.prototype._history; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWG1sUGFyc2VyLmpzIiwic291cmNlUm9vdCI6Im5nOi8vQG1hZ2ljLXhwYS91dGlscy8iLCJzb3VyY2VzIjpbInNyYy9YbWwvWG1sUGFyc2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQWMsS0FBSyxFQUFFLE9BQU8sRUFBQyxNQUFNLHNCQUFzQixDQUFDO0FBQ3BILE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQzs7QUFHN0M7SUFPRSxhQUFhO0lBQ2IsR0FBRztJQUNILGNBQWM7SUFDZCwrQkFBK0I7SUFDL0IsbUJBQVksSUFBNEI7UUFBNUIscUJBQUEsRUFBQSxPQUFlLE9BQU8sQ0FBQyxLQUFLO1FBUmhDLGVBQVUsR0FBVyxDQUFDLENBQUM7UUFDdkIsYUFBUSxHQUFXLEVBQUUsQ0FBQztRQUN0QixhQUFRLEdBQTBCLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQyx3REFBd0Q7UUFPNUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN0QixJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCx5R0FBeUc7SUFDekcsOERBQThEO0lBQzlELG9GQUFvRjtJQUNwRiwrSUFBK0k7SUFDL0ksa0hBQWtIOzs7Ozs7Ozs7Ozs7SUFDM0csbUJBQVM7Ozs7Ozs7Ozs7OztJQUFoQixVQUFpQixHQUFXLEVBQUUsU0FBaUIsRUFBRSxVQUEwQjtRQUExQiwyQkFBQSxFQUFBLGlCQUEwQjs7WUFDckUsU0FBUyxHQUFpQixJQUFJLElBQUksRUFBVTs7WUFDNUMsS0FBSyxHQUFXLElBQUk7UUFFeEIsRUFBRSxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztZQUNmLEdBQUcsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDbkIsQ0FBQzs7WUFFRyxNQUFNLEdBQWEsR0FBRyxD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RSxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBOVpjLHVCQUFhLEdBQWEsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7SUErWnRELGdCQUFDO0NBQUEsQUFoYUQsSUFnYUM7U0FoYVksU0FBUzs7O0lBQ3BCLHdCQUFvRDs7SUFFcEQsK0JBQStCOztJQUMvQiw2QkFBOEI7O0lBQzlCLDZCQUFxRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7TlN0cmluZywgTGlzdCwgQXBwbGljYXRpb25FeGNlcHRpb24sIFN0cmluZ0J1aWxkZXIsIERpY3Rpb25hcnksIE5DaGFyLCBOTnVtYmVyfSBmcm9tIFwiQG1hZ2ljLXhwYS9tc2NvcmVsaWJcIjtcclxuaW1wb3J0IHtYTUxDb25zdGFudHN9IGZyb20gXCIuLi9YTUxDb25zdGFudHNcIjtcclxuXHJcbi8vLyA8c3VtbWFyeT4gYSBoZWxwZXIgY2xhc3MgZm9yIHRoZSBwYXJzaW5nIG9mIHRoZSBYTUw8L3N1bW1hcnk+XHJcbmV4cG9ydCBjbGFzcyBYbWxQYXJzZXIge1xyXG4gIHByaXZhdGUgc3RhdGljIGVuZE9mTmFtZUNoYXI6IHN0cmluZ1tdID0gWycgJywgJz4nXTtcclxuXHJcbiAgcHJpdmF0ZSBfY3VyckluZGV4OiBudW1iZXIgPSAwO1xyXG4gIHByaXZhdGUgX3htTGRhdGE6IHN0cmluZyA9IFwiXCI7XHJcbiAgcHJpdmF0ZSBfaGlzdG9yeTogTGlzdDxudW1iZXIgfCBzdHJpbmc+ID0gbmV3IExpc3QoKTsgLy8gSW4gb3JkZXIgdG8gYWxsb3cgcmVjdXJzaXZlIHBhcnNpbmcgd2Ugc2F2ZSBwcmV2IGRhdGFcclxuXHJcbiAgLy8vIDxzdW1tYXJ5PlxyXG4gIC8vL1xyXG4gIC8vLyA8L3N1bW1hcnk+XHJcbiAgLy8vIDxwYXJhbSBuYW1lPVwiZGF0YVwiPjwvcGFyYW0+XHJcbiAgY29uc3RydWN0b3IoZGF0YTogc3RyaW5nID0gTlN0cmluZy5FbXB0eSkge1xyXG4gICAgdGhpcy5zZXRYTUxkYXRhKGRhdGEpO1xyXG4gICAgdGhpcy5zZXRDdXJySW5kZXgoMCk7XHJcbiAgfVxyXG5cclxuICAvLy8gPHN1bW1hcnk+IHBhcnNlIGEgc3RyaW5nIGFjY29yZGluZyB0byBhIHNldCBvZiBkZWxpbWl0ZXJzIGFuZCByZXR1cm4gdGhlIHJlc3VsdCBpbiBhIHZlY3Rvcjwvc3VtbWFyeT5cclxuICAvLy8gPHBhcmFtIG5hbWU9XCJzdHJcIj50aGUgU3RyaW5nIHdoaWNoIG5lZWQgYmUgcGFydGVkIDwvcGFyYW0+XHJcbiAgLy8vIDxwYXJhbSBuYW1lPVwiZGVsaW1pdGVyXCI+dGhlIGRlbGltaXRlciB3aGljaCBwYXJ0IGRpZmZlcmVudCBwYXJ0cyBvZiBzdHIgPC9wYXJhbT5cclxuICAvLy8gPHBhcmFtIG5hbWU9XCJpc01hZ2ljWE1MXCI+aXMgbmVlZGVkIHRva2VuaXplciB3b3JraW5nIG9uIE1hZ2ljIFhNTCwgc28gdGhlIFwiPVwiIHNpZ24gd2lsbCBiZSBkZWxpdGVkIGluIHRoZSBlbmQgb2YgZXZlcnkgZmlyc3QgdG9rZW4gPC9wYXJhbT5cclxuICAvLy8gPHJldHVybnM+IHRtcFZlY3RvciBkeW5hbWljYWxseSBhcnJheSwgd2hpY2ggY29uc2lzdCB0b2tlbnMgaW4gZXZlcnkgZWxlbWVudCwgZXZlcnkgdG9rZW4gaXMgU3RyaW5nIDwvcmV0dXJucz5cclxuICBzdGF0aWMgZ2V0VG9rZW5zKHN0cjogc3RyaW5nLCBkZWxpbWl0ZXI6IHN0cmluZywgaXNNYWdpY1hNTDogYm9vbGVhbiA9IHRydWUpOiBMaXN0PHN0cmluZz4ge1xyXG4gICAgbGV0IHRva2Vuc1ZlYzogTGlzdDxzdHJpbmc+ID0gbmV3IExpc3Q8c3RyaW5nPigpO1xyXG4gICAgbGV0IHRva2VuOiBzdHJpbmcgPSBudWxsO1xyXG5cclxuICAgIGlmIChpc01hZ2ljWE1MKSB7XHJcbiAgICAgIHN0ciA9IHN0ci50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IHN0clRvazogc3RyaW5nW10gPSBzdHIuc3BsaXQoZGVsaW1pdGVyLmNoYXJBdCgwKSk7XHJcblxyXG4gICAgZm9yIChsZXQgaTogbnVtYmVyID0gMDsgaSA8IHN0clRvay5sZW5ndGg7IGkgPSBpICsgMSkge1xyXG4gICAgICAvLyBTcGxpdCBpbiBDIyBjcmVhdGVzIGEgbGFzdCBlbXB0eSBzdHJpbmcgdG9rZW4gaWYgdGhlIHNvdXJjZSBzdHJpbmcgZW5kcyB3aXRoXHJcbiAgICAgIC8vIHRoZSBkZWxpbWl0ZXIgb3IgaWYgdGhlIHN0cmluZyBpcyBlbXB0eSAoYXMgb3Bwb3NlZCB0byBKYXZhIHRoYXQgd2lsbCBpZ25vcmUgaXQpXHJcbiAgICAgIC8vIHRoZXJlZm9yZSB3ZSBoYXZlIHRvIGJyZWFrIHRoaXMgbG9vcCBpZiBzdWNoIGNhc2Ugb2NjdXJzLlxyXG4gICAgICBpZiAoaXNNYWdpY1hNTCAmJiBpID09PSBzdHJUb2subGVuZ3RoIC0gMSAmJiBzdHJUb2subGVuZ3RoICUgMiA9PT0gMSkge1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICB9XHJcblxyXG4gICAgICB0b2tlbiA9IHN0clRva1tpXTtcclxuICAgICAgaWYgKGlzTWFnaWNYTUwpIHtcclxuICAgICAgICAvLyB0aGUgMXN0IHRva2VuIGluIHRoZSBwYWlyIGNvbWVzIHdpdGggXCI9XCIsIHJlbW92ZSBpdC5cclxuICAgICAgICBpZiAoaSAlIDIgPT09IDApIHtcclxuICAgICAgICAgIHRva2VuID0gdG9rZW4udHJpbSgpO1xyXG4gICAgICAgICAgaWYgKHRva2VuLmVuZHNXaXRoKFwiPVwiKSkge1xyXG4gICAgICAgICAgICB0b2tlbiA9IHRva2VuLnN1YnN0cigwLCB0b2tlbi5sZW5ndGggLSAxKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgLy8gMm5kIHRva2VuIGluIHRoZSBwYWlyIGNhbiBiZSBhbiBlbXB0eSBzdHJpbmcsIGluIHRoYXQgY2FzZSBzZXQgaXQgdG8gXCIgXCIuXHJcbiAgICAgICAgZWxzZSBpZiAodG9rZW4gPT09IFwiXCIpXHJcbiAgICAgICAgICB0b2tlbiA9IFwiIFwiO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAodG9rZW4gPT09IG51bGwpXHJcbiAgICAgICAgdGhyb3cgbmV3IEFwcGxpY2F0aW9uRXhjZXB0aW9uKFwiaW4gQ2xpZW50TWFuYWdlci5JbnN0YW5jZS5YTUxQYXJzZXIuZ2V0VG9rZW5zKCkgbnVsbCB0b2tlbiB2YWx1ZVwiKTtcclxuXHJcbiAgICAgIHRva2Vuc1ZlYy5wdXNoKHRva2VuKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gdG9rZW5zVmVjO1xyXG4gIH1cclxuXHJcbiAgLy8vIDxzdW1tYXJ5PnVuc2NhcGUgZnJvbTpcclxuICAvLy8ge1wiJmFtcDtcIixcXFxcLCBcXHEsIFxcbywgXFxsLCBcXGcsIFxcZSwgXFxcXHIsIFxcXFxufSwgdG86XHJcbiAgLy8vIHtcIiZcIiwgICAgIFxcLCAgXCIsICAnLCAgPCwgID4sICA9LCAgXFxyLCAgXFxufVxyXG4gIC8vLyA8cGFyYW0gbmFtZT1cInN0clwiPlN0cmluZyB0byBiZSBjb252ZXJ0ZWQ8L3BhcmFtPlxyXG4gIC8vLyA8cmV0dXJucz51bmVzY2FwZWQgc3RyaW5nPC9yZXR1cm5zPlxyXG4gIHB1YmxpYyBzdGF0aWMgdW5lc2NhcGUoc3RyOiBzdHJpbmcpOiBzdHJpbmdcclxuICB7XHJcbiAgICBsZXQgdW5lc2NhcGVkU3RyaW5nOiBTdHJpbmdCdWlsZGVyID0gbmV3IFN0cmluZ0J1aWxkZXIoc3RyLmxlbmd0aCk7XHJcblxyXG4gICAgZm9yIChsZXQgaTogbnVtYmVyID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xyXG4gICAgICBpZiAoc3RyW2ldICE9PSAnXFxcXCcpIHtcclxuICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKHN0cltpXSk7XHJcbiAgICAgICAgY29udGludWU7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHN3aXRjaCAoc3RyWysraV0pIHtcclxuICAgICAgICBjYXNlICdxJzpcclxuICAgICAgICAgIHVuZXNjYXBlZFN0cmluZy5BcHBlbmQoJ1xcXCInKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgJ28nOlxyXG4gICAgICAgICAgdW5lc2NhcGVkU3RyaW5nLkFwcGVuZCgnXFwnJyk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdsJzpcclxuICAgICAgICAgIHVuZXNjYXBlZFN0cmluZy5BcHBlbmQoJzwnKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgJ2cnOlxyXG4gICAgICAgICAgdW5lc2NhcGVkU3RyaW5nLkFwcGVuZCgnPicpO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnZSc6XHJcbiAgICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKCc9Jyk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdyJzpcclxuICAgICAgICAgIHVuZXNjYXBlZFN0cmluZy5BcHBlbmQoJ1xccicpO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnbic6XHJcbiAgICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKCdcXG4nKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKHN0cltpXSk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuICh1bmVzY2FwZWRTdHJpbmcuVG9TdHJpbmcoKSk7XHJcbiAgfVxyXG5cclxuICAvLy8gPHN1bW1hcnk+ZXNjYXBlIGZyb206XHJcbiAgLy8vIHtcXCwgIFwiLCAgJywgIDwsICAgPiwgID0sICBcXHIsICBcXG59LCB0bzpcclxuICAvLy8ge1xcXFwsIFxccSwgXFwwLCBcXGwsICBcXGcsIFxcZSwgXFxcXHIsIFxcXFxufVxyXG4gIC8vLyA8cGFyYW0gbmFtZT1cInN0clwiPlN0cmluZyB0byBiZSBjb252ZXJ0ZWQ8L3BhcmFtPlxyXG4gIC8vLyA8cmV0dXJucz5lc2NhcGVkIHN0cmluZzwvcmV0dXJucz5cclxuICBwdWJsaWMgc3RhdGljIGVzY2FwZShzdHI6IHN0cmluZyk6IHN0cmluZ1xyXG4gIHtcclxuICAgIGxldCBlc2NhcGVkU3RyaW5nOiBTdHJpbmdCdWlsZGVyID0gbmV3IFN0cmluZ0J1aWxkZXIoc3RyLmxlbmd0aCAqIDIpO1xyXG5cclxuICAgIGZvciAobGV0IGk6IG51bWJlciA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHtcclxuICAgICAgc3dpdGNoIChzdHJbaV0pIHtcclxuICAgICAgICBjYXNlICdcXFxcJzpcclxuICAgICAgICAgIGVzY2FwZWRTdHJpbmcuQXBwZW5kKFwiXFxcXFxcXFxcIik7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdcIic6XHJcbiAgICAgICAgICBlc2NhcGVkU3RyaW5nLkFwcGVuZChcIlxcXFxxXCIpO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnXFwnJzpcclxuICAgICAgICAgIGVzY2FwZWRTdHJpbmcuQXBwZW5kKFwiXFxcXG9cIik7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICc8Jz