text: var __extends = (this && this.__extends) || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  };
  // Custom implementation of Globalize from PowerView team
  // The public implementation from https://github.com/borisyankov/DefinitelyTyped/tree/master/globalize doesn't work
  /* tslint:disable:no-var-keyword */
  var Globalize = Globalize || window["Globalize"];
  /* tslint:enable */
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var LocalStorageService = (function () {
                      function LocalStorageService() {
                      }
                      LocalStorageService.prototype.getData = function (key) {
                          try {
                              if (localStorage) {
                                  var value = localStorage[key];
                                  if (value) {
                                      return JSON.parse(value);
                                  }
                              }
                          }
                          catch (exception) { }
                          return null;
                      };
                      LocalStorageService.prototype.setData = function (key, data) {
                          try {
                              if (localStorage) {
                                  localStorage[key] = JSON.stringify(data);
                              }
                          }
                          catch (e) { }
                      };
                      return LocalStorageService;
                  }());
                  formatting.LocalStorageService = LocalStorageService;
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var EphemeralStorageService = (function () {
                      function EphemeralStorageService(clearCacheInterval) {
                          this.cache = {};
                          this.clearCacheInterval = (clearCacheInterval != null)
                              ? clearCacheInterval
                              : EphemeralStorageService.defaultClearCacheInterval;
                          this.clearCache();
                      }
                      EphemeralStorageService.prototype.getData = function (key) {
                          return this.cache[key];
                      };
                      EphemeralStorageService.prototype.setData = function (key, data) {
                          var _this = this;
                          this.cache[key] = data;
                          if (this.clearCacheTimerId == null) {
                              this.clearCacheTimerId = setTimeout(function () { return _this.clearCache(); }, this.clearCacheInterval);
                          }
                      };
                      EphemeralStorageService.prototype.clearCache = function () {
                          this.cache = {};
                          this.clearCacheTimerId = undefined;
                      };
                      EphemeralStorageService.defaultClearCacheInterval = (1000 * 60 * 60 * 24); // 1 day
                      return EphemeralStorageService;
                  }());
                  formatting.EphemeralStorageService = EphemeralStorageService;
                  formatting.ephemeralStorageService = new EphemeralStorageService();
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  /**
                   * Extensions to String class.
                   */
                  var stringExtensions;
                  (function (stringExtensions) {
                      var HtmlTagRegex = new RegExp("[<>]", "g");
                      /**
                       * Checks if a string ends with a sub-string.
                       */
                      function endsWith(str, suffix) {
                          return str.indexOf(suffix, str.length - suffix.length) !== -1;
                      }
                      stringExtensions.endsWith = endsWith;
                      function format() {
                          var args = [];
                          for (var _i = 0; _i < arguments.length; _i++) {
                              args[_i - 0] = arguments[_i];
                          }
                          var s = args[0];
                          if (isNullOrUndefinedOrWhiteSpaceString(s))
                              return s;
                          for (var i = 0; i < args.length - 1; i++) {
                              var reg = new RegExp("\\{" + i + "\\}", "gm");
                              s = s.replace(reg, args[i + 1]);
                          }
                          return s;
                      }
                      stringExtensions.format = format;
                      /**
                       * Compares two strings for equality, ignoring case.
                       */
                      function equalIgnoreCase(a, b) {
                          return stringExtensions.normalizeCase(a) === stringExtensions.normalizeCase(b);
                      }
                      stringExtensions.equalIgnoreCase = equalIgnoreCase;
                      function startsWithIgnoreCase(a, b) {
                          var normalizedSearchString = stringExtensions.normalizeCase(b);
                          return stringExtensions.normalizeCase(a).indexOf(normalizedSearchString) === 0;
                      }
                      stringExtensions.startsWithIgnoreCase = startsWithIgnoreCase;
                      function startsWith(a, b) {
                          return a.indexOf(b) === 0;
                      }
                      stringExtensions.startsWith = startsWith;
                      /** Determines whether a string contains a specified substring (by case-sensitive comparison). */
                      function contains(source, substring) {
                          if (source == null)
                              return false;
                          return source.indexOf(substring) !== -1;
                      }
                      stringExtensions.contains = contains;
                      /** Determines whether a string contains a specified substring (while ignoring case). */
                      function containsIgnoreCase(source, substring) {
                          if (source == null)
                              return false;
                          return contains(normalizeCase(source), normalizeCase(substring));
                      }
                      stringExtensions.containsIgnoreCase = containsIgnoreCase;
                      /**
                       * Normalizes case for a string.
                       * Used by equalIgnoreCase method.
                       */
                      function normalizeCase(value) {
                          return value.toUpperCase();
                      }
                      stringExtensions.normalizeCase = normalizeCase;
                      /**
                       * Receives a string and returns an ArrayBuffer of its characters.
                       * @return An ArrayBuffer of the string's characters.
                       * If the string is empty or null or undefined - returns null.
                       */
                      function stringToArrayBuffer(str) {
                          if (isNullOrEmpty(str)) {
                              return null;
                          }
                          var buffer = new ArrayBuffer(str.length);
                          var bufferView = new Uint8Array(buffer);
                          for (var i = 0, strLen = str.length; i < strLen; i++) {
                              bufferView[i] = str.charCodeAt(i);
                          }
                          return bufferView;
                      }
                      stringExtensions.stringToArrayBuffer = stringToArrayBuffer;
                      /**
                       * Is string null or empty or undefined?
                       * @return True if the value is null or undefined or empty string,
                       * otherwise false.
                       */
                      function isNullOrEmpty(value) {
                          return (value == null) || (value.length === 0);
                      }
                      stringExtensions.isNullOrEmpty = isNullOrEmpty;
                      /**
                       * Returns true if the string is null, undefined, empty, or only includes white spaces.
                       * @return True if the str is null, undefined, empty, or only includes white spaces,
                       * otherwise false.
                       */
                      function isNullOrUndefinedOrWhiteSpaceString(str) {
                          return stringExtensions.isNullOrEmpty(str) || stringExtensions.isNullOrEmpty(str.trim());
                      }
                      stringExtensions.isNullOrUndefinedOrWhiteSpaceString = isNullOrUndefinedOrWhiteSpaceString;
                      /**
                       * Returns a value indicating whether the str contains any whitespace.
                       */
                      function containsWhitespace(str) {
                          var expr = /\s/;
                          return expr.test(str);
                      }
                      stringExtensions.containsWhitespace = containsWhitespace;
                      /**
                       * Returns a value indicating whether the str is a whitespace string.
                       */
                      function isWhitespace(str) {
                          return str.trim() === "";
                      }
                      stringExtensions.isWhitespace = isWhitespace;
                      /**
                       * Returns the string with any trailing whitespace from str removed.
                       */
                      function trimTrailingWhitespace(str) {
                          return str.replace(/\s+$/, "");
                      }
                      stringExtensions.trimTrailingWhitespace = trimTrailingWhitespace;
                      /**
                       * Returns the string with any leading and trailing whitespace from str removed.
                       */
                      function trimWhitespace(str) {
                          return str.replace(/^\s+/, "").replace(/\s+$/, "");
                      }
                      stringExtensions.trimWhitespace = trimWhitespace;
                      /**
                       * Returns length difference between the two provided strings.
                       */
                      function getLengthDifference(left, right) {
                          return Math.abs(left.length - right.length);
                      }
                      stringExtensions.getLengthDifference = getLengthDifference;
                      /**
                       * Repeat char or string several times.
                       * @param char The string to repeat.
                       * @param count How many times to repeat the string.
                       */
                      function repeat(char, count) {
                          var result = "";
                          for (var i = 0; i < count; i++) {
                              result += char;
                          }
                          return result;
                      }
                      stringExtensions.repeat = repeat;
                      /**
                       * Replace all the occurrences of the textToFind in the text with the textToReplace.
                       * @param text The original string.
                       * @param textToFind Text to find in the original string.
                       * @param textToReplace New text replacing the textToFind.
                       */
                      function replaceAll(text, textToFind, textToReplace) {
                          if (!textToFind)
                              return text;
                          var pattern = escapeStringForRegex(textToFind);
                          return text.replace(new RegExp(pattern, "gi"), textToReplace);
                      }
                      stringExtensions.replaceAll = replaceAll;
                      function ensureUniqueNames(names) {
                          var usedNames = {};
                          // Make sure we are giving fair chance for all columns to stay with their original name
                          // First we fill the used names map to contain all the original unique names from the list.
                          for (var _i = 0, names_1 = names; _i < names_1.length; _i++) {
                              var name_1 = names_1[_i];
                              usedNames[name_1] = false;
                          }
                          var uniqueNames = [];
                          // Now we go over all names and find a unique name for each
                          for (var _a = 0, names_2 = names; _a < names_2.length; _a++) {
                              var name_2 = names_2[_a];
                              var uniqueName = name_2;
                              // If the (original) column name is already taken lets try to find another name
                              if (usedNames[uniqueName]) {
                                  var counter = 0;
                                  // Find a name that is not already in the map
                                  while (usedNames[uniqueName] !== undefined) {
                                      uniqueName = name_2 + "." + (++counter);
                                  }
                              }
                              uniqueNames.push(uniqueName);
                              usedNames[uniqueName] = true;
                          }
                          return uniqueNames;
                      }
                      stringExtensions.ensureUniqueNames = ensureUniqueNames;
                      /**
                       * Returns a name that is not specified in the values.
                       */
                      function findUniqueName(usedNames, baseName) {
                          // Find a unique name
                          var i = 0, uniqueName = baseName;
                          while (usedNames[uniqueName]) {
                              uniqueName = baseName + (++i);
                          }
                          return uniqueName;
                      }
                      stringExtensions.findUniqueName = findUniqueName;
                      function constructNameFromList(list, separator, maxCharacter) {
                          var labels = [];
                          var exceeded;
                          var length = 0;
                          for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
                              var item = list_1[_i];
                              if (length + item.length > maxCharacter && labels.length > 0) {
                                  exceeded = true;
                                  break;
                              }
                              labels.push(item);
                              length += item.length;
                          }
                          var separatorWithSpace = " " + separator + " ";
                          var name = labels.join(separatorWithSpace);
                          if (exceeded)
                              name += separatorWithSpace + "...";
                          return name;
                      }
                      stringExtensions.constructNameFromList = constructNameFromList;
                      function escapeStringForRegex(s) {
                          return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, "\\$1");
                      }
                      stringExtensions.escapeStringForRegex = escapeStringForRegex;
                      /**
                       * Remove file name reserved characters <>:"/\|?* from input string.
                       */
                      function normalizeFileName(fileName) {
                          return fileName.replace(/[\<\>\:"\/\\\|\?*]/g, "");
                      }
                      stringExtensions.normalizeFileName = normalizeFileName;
                      /**
                       * Similar to JSON.stringify, but strips away escape sequences so that the resulting
                       * string is human-readable (and parsable by JSON formatting/validating tools).
                       */
                      function stringifyAsPrettyJSON(object) {
                          // let specialCharacterRemover = (key: string, value: string) => value.replace(/[^\w\s]/gi, "");
                          return JSON.stringify(object /*, specialCharacterRemover*/);
                      }
                      stringExtensions.stringifyAsPrettyJSON = stringifyAsPrettyJSON;
                      /**
                       * Derive a CLS-compliant name from a specified string.  If no allowed characters are present, return a fallback string instead.
                       * TODO (6708134): this should have a fully Unicode-aware implementation
                       */
                      function deriveClsCompliantName(input, fallback) {
                          var result = input.replace(/^[^A-Za-z]*/g, "").replace(/[ :\.\/\\\-\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]/g, "_").replace(/[\W]/g, "");
                          return result.length > 0 ? result : fallback;
                      }
                      stringExtensions.deriveClsCompliantName = deriveClsCompliantName;
                      /** Performs cheap sanitization by stripping away HTML tag (<>) characters. */
                      function stripTagDelimiters(s) {
                          return s.replace(HtmlTagRegex, "");
                      }
                      stringExtensions.stripTagDelimiters = stripTagDelimiters;
                  })(stringExtensions = formatting.stringExtensions || (formatting.stringExtensions = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var wordBreaker;
                  (function (wordBreaker) {
                      var SPACE = " ";
                      var BREAKERS_REGEX = /[\s\n]+/g;
                      function search(index, content, backward) {
                          if (backward) {
                              for (var i = index - 1; i > -1; i--) {
                                  if (hasBreakers(content[i]))
                                      return i + 1;
                              }
                          }
                          else {
                              for (var i = index, ilen = content.length; i < ilen; i++) {
                                  if (hasBreakers(content[i]))
                                      return i;
                              }
                          }
                          return backward ? 0 : content.length;
                      }
                      /**
                       * Find the word nearest the cursor specified within content
                       * @param index - point within content to search forward/backward from
                       * @param content - string to search
                      */
                      function find(index, content) {
                          var result = { start: 0, end: 0 };
                          if (content.length === 0) {
                              return result;
                          }
                          result.start = search(index, content, true);
                          result.end = search(index, content, false);
                          return result;
                      }
                      wordBreaker.find = find;
                      /**
                       * Test for presence of breakers within content
                       * @param content - string to test
                      */
                      function hasBreakers(content) {
                          BREAKERS_REGEX.lastIndex = 0;
                          return BREAKERS_REGEX.test(content);
                      }
                      wordBreaker.hasBreakers = hasBreakers;
                      /**
                       * Count the number of pieces when broken by BREAKERS_REGEX
                       * ~2.7x faster than WordBreaker.split(content).length
                       * @param content - string to break and count
                      */
                      function wordCount(content) {
                          var count = 1;
                          BREAKERS_REGEX.lastIndex = 0;
                          BREAKERS_REGEX.exec(content);
                          while (BREAKERS_REGEX.lastIndex !== 0) {
                              count++;
                              BREAKERS_REGEX.exec(content);
                          }
                          return count;
                      }
                      wordBreaker.wordCount = wordCount;
                      function getMaxWordWidth(content, textWidthMeasurer, properties) {
                          var words = split(content);
                          var maxWidth = 0;
                          for (var _i = 0, words_1 = words; _i < words_1.length; _i++) {
                              var w = words_1[_i];
                              properties.text = w;
                              maxWidth = Math.max(maxWidth, textWidthMeasurer(properties));
                          }
                          return maxWidth;
                      }
                      wordBreaker.getMaxWordWidth = getMaxWordWidth;
                      function split(content) {
                          return content.split(BREAKERS_REGEX);
                      }
                      function getWidth(content, properties, textWidthMeasurer) {
                          properties.text = content;
                          return textWidthMeasurer(properties);
                      }
                      function truncate(content, properties, truncator, maxWidth) {
                          properties.text = content;
                          return truncator(properties, maxWidth);
                      }
                      /**
                       * Split content by breakers (words) and greedy fit as many words
                       * into each index in the result based on max width and number of lines
                       * e.g. Each index in result corresponds to a line of content
                       *      when used by AxisHelper.LabelLayoutStrategy.wordBreak
                       * @param content - string to split
                       * @param properties - text properties to be used by @param:textWidthMeasurer
                       * @param textWidthMeasurer - function to calculate width of given text content
                       * @param maxWidth - maximum allowed width of text content in each result
                       * @param maxNumLines - maximum number of results we will allow, valid values must be greater than 0
                       * @param truncator - (optional) if specified, used as a function to truncate content to a given width
                      */
                      function splitByWidth(content, properties, textWidthMeasurer, maxWidth, maxNumLines, truncator) {
                          // Default truncator returns string as-is
                          truncator = truncator ? truncator : function (properties, maxWidth) { return properties.text; };
                          var result = [];
                          var words = split(content);
                          var usedWidth = 0;
                          var wordsInLine = [];
                          for (var _i = 0, words_2 = words; _i < words_2.length; _i++) {
                              var word = words_2[_i];
                              // Last line? Just add whatever is left
                              if ((maxNumLines > 0) && (result.length >= maxNumLines - 1)) {
                                  wordsInLine.push(word);
                                  continue;
                              }
                              // Determine width if we add this word
                              // Account for SPACE we will add when joining...
                              var wordWidth = wordsInLine.length === 0
                                  ? getWidth(word, properties, textWidthMeasurer)
                                  : getWidth(SPACE + word, properties, textWidthMeasurer);
                              // If width would exceed max width,
                              // then push used words and start new split result
                              if (usedWidth + wordWidth > maxWidth) {
                                  // Word alone exceeds max width, just add it.
                                  if (wordsInLine.length === 0) {
                                      result.push(truncate(word, properties, truncator, maxWidth));
                                      usedWidth = 0;
                                      wordsInLine = [];
                                      continue;
                                  }
                                  result.push(truncate(wordsInLine.join(SPACE), properties, truncator, maxWidth));
                                  usedWidth = 0;
                                  wordsInLine = [];
                              }
                              // ...otherwise, add word and continue
                              wordsInLine.push(word);
                              usedWidth += wordWidth;
                          }
                          // Push remaining words onto result (if any)
                          if (!_.isEmpty(wordsInLine)) {
                              result.push(truncate(wordsInLine.join(SPACE), properties, truncator, maxWidth));
                          }
                          return result;
                      }
                      wordBreaker.splitByWidth = splitByWidth;
                  })(wordBreaker = formatting.wordBreaker || (formatting.wordBreaker = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  /** Enumeration of DateTimeUnits */
                  (function (DateTimeUnit) {
                      DateTimeUnit[DateTimeUnit["Year"] = 0] = "Year";
                      DateTimeUnit[DateTimeUnit["Month"] = 1] = "Month";
                      DateTimeUnit[DateTimeUnit["Week"] = 2] = "Week";
                      DateTimeUnit[DateTimeUnit["Day"] = 3] = "Day";
                      DateTimeUnit[DateTimeUnit["Hour"] = 4] = "Hour";
                      DateTimeUnit[DateTimeUnit["Minute"] = 5] = "Minute";
                      DateTimeUnit[DateTimeUnit["Second"] = 6] = "Second";
                      DateTimeUnit[DateTimeUnit["Millisecond"] = 7] = "Millisecond";
                  })(formatting.DateTimeUnit || (formatting.DateTimeUnit = {}));
                  var DateTimeUnit = formatting.DateTimeUnit;
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var createClassAndSelector = powerbi.extensibility.utils.svg.CssConstants.createClassAndSelector;
                  // powerbi.extensibility.utils.type
                  var PixelConverter = powerbi.extensibility.utils.type.PixelConverter;
                  var Prototype = powerbi.extensibility.utils.type.Prototype;
                  // powerbi.extensibility.utils.formatting
                  var wordBreaker = powerbi.extensibility.utils.formatting.wordBreaker;
                  var textMeasurementService;
                  (function (textMeasurementService) {
                      var ellipsis = "...";
                      var OverflowingText = createClassAndSelector("overflowingText");
                      var spanElement;
                      var svgTextElement;
                      var canvasCtx;
                      var fallbackFontFamily;
                      /**
                       * Idempotent function for adding the elements to the DOM.
                       */
                      function ensureDOM() {
                          if (spanElement) {
                              return;
                          }
                          spanElement = $("<span/>");
                          $("body").append(spanElement);
                          // The style hides the svg element from the canvas, preventing canvas from scrolling down to show svg black square.
                          svgTextElement = d3.select($("body").get(0))
                              .append("svg")
                              .style({
                              "height": "0px",
                              "width": "0px",
                              "position": "absolute"
                          })
                              .append("text");
                          canvasCtx = $("<canvas/>").get(0).getContext("2d");
                          var style = window.getComputedStyle(svgTextElement.node());
                          if (style) {
                              fallbackFontFamily = style.fontFamily;
                          }
                          else {
                              fallbackFontFamily = "";
                          }
                      }
                      /**
                       * Removes spanElement from DOM.
                       */
                      function removeSpanElement() {
                          if (spanElement && spanElement.remove) {
                              spanElement.remove();
                          }
                          spanElement = null;
                      }
                      textMeasurementService.removeSpanElement = removeSpanElement;
                      /**
                       * This method measures the width of the text with the given SVG text properties.
                       * @param textProperties The text properties to use for text measurement.
                       * @param text The text to measure.
                       */
                      function measureSvgTextWidth(textProperties, text) {
                          ensureDOM();
                          canvasCtx.font =
                              (textProperties.fontStyle || "") + " " +
                                  (textProperties.fontVariant || "") + " " +
                                  (textProperties.fontWeight || "") + " " +
                                  textProperties.fontSize + " " +
                                  (textProperties.fontFamily || fallbackFontFamily);
                          return canvasCtx.measureText(text || textProperties.text).width;
                      }
                      textMeasurementService.measureSvgTextWidth = measureSvgTextWidth;
                      /**
                       * This method return the rect with the given SVG text properties.
                       * @param textProperties The text properties to use for text measurement.
                       * @param text The text to measure.
                       */
                      function measureSvgTextRect(textProperties, text) {
                          ensureDOM();
                          svgTextElement.style(null);
                          svgTextElement
                              .text(text || textProperties.text)
                              .attr({
                              "visibility": "hidden",
                              "font-family": textProperties.fontFamily || fallbackFontFamily,
                              "font-variant": textProperties.fontVariant,
                              "font-size": textProperties.fontSize,
                              "font-weight": textProperties.fontWeight,
                              "font-style": textProperties.fontStyle,
                              "white-space": textProperties.whiteSpace || "nowrap"
                          });
                          // We're expecting the browser to give a synchronous measurement here
                          // We're using SVGTextElement because it works across all browsers
                          return svgTextElement.node().getBBox();
                      }
                      textMeasurementService.measureSvgTextRect = measureSvgTextRect;
                      /**
                       * This method measures the height of the text with the given SVG text properties.
                       * @param textProperties The text properties to use for text measurement.
                       * @param text The text to measure.
                       */
                      function measureSvgTextHeight(textProperties, text) {
                          return measureSvgTextRect(textProperties, text).height;
                      }
                      textMeasurementService.measureSvgTextHeight = measureSvgTextHeight;
                      /**
                       * This method returns the text Rect with the given SVG text properties.
                       * Does NOT return text width; obliterates text value
                       * @param {TextProperties} textProperties - The text properties to use for text measurement
                       */
                      function estimateSvgTextRect(textProperties) {
                          var propertiesKey = textProperties.fontFamily + textProperties.fontSize;
                          var rect = formatting.ephemeralStorageService.getData(propertiesKey);
                          if (rect == null) {
                              // To estimate we check the height of a particular character, once it is cached, subsequent
                              // calls should always get the height from the cache (regardless of the text).
                              var estimatedTextProperties = {
                                  fontFamily: textProperties.fontFamily,
                                  fontSize: textProperties.fontSize,
                                  text: "M",
                              };
                              rect = textMeasurementService.measureSvgTextRect(estimatedTextProperties);
                              // NOTE: In some cases (disconnected/hidden DOM) we may provide incorrect measurement results (zero sized bounding-box), so
                              // we only store values in the cache if we are confident they are correct.
                              if (rect.height > 0)
                                  formatting.ephemeralStorageService.setData(propertiesKey, rect);
                          }
                          return rect;
                      }
                      /**
                       * This method returns the text Rect with the given SVG text properties.
                       * @param {TextProperties} textProperties - The text properties to use for text measurement
                       */
                      function estimateSvgTextBaselineDelta(textProperties) {
                          var rect = estimateSvgTextRect(textProperties);
                          return rect.y + rect.height;
                      }
                      textMeasurementService.estimateSvgTextBaselineDelta = estimateSvgTextBaselineDelta;
                      /**
                       * This method estimates the height of the text with the given SVG text properties.
                       * @param {TextProperties} textProperties - The text properties to use for text measurement
                       */
                      function estimateSvgTextHeight(textProperties, tightFightForNumeric) {
                          if (tightFightForNumeric === void 0) { tightFightForNumeric = false; }
                          var height = estimateSvgTextRect(textProperties).height;
                          // TODO: replace it with new baseline calculation
                          if (tightFightForNumeric)
                              height *= 0.7;
                          return height;
                      }
                      textMeasurementService.estimateSvgTextHeight = estimateSvgTextHeight;
                      /**
                       * This method measures the width of the svgElement.
                       * @param svgElement The SVGTextElement to be measured.
                       */
                      function measureSvgTextElementWidth(svgElement) {
                          return measureSvgTextWidth(getSvgMeasurementProperties(svgElement));
                      }
                      textMeasurementService.measureSvgTextElementWidth = measureSvgTextElementWidth;
                      /**
                       * This method fetches the text measurement properties of the given DOM element.
                       * @param element The selector for the DOM Element.
                       */
                      function getMeasurementProperties(element) {
                          return {
                              text: element.val() || element.text(),
                              fontFamily: element.css("font-family"),
                              fontSize: element.css("font-size"),
                              fontWeight: element.css("font-weight"),
                              fontStyle: element.css("font-style"),
                              fontVariant: element.css("font-variant"),
                              whiteSpace: element.css("white-space")
                          };
                      }
                      textMeasurementService.getMeasurementProperties = getMeasurementProperties;
                      /**
                       * This method fetches the text measurement properties of the given SVG text element.
                       * @param svgElement The SVGTextElement to be measured.
                       */
                      function getSvgMeasurementProperties(svgElement) {
                          var style = window.getComputedStyle(svgElement, null);
                          if (style) {
                              return {
                                  text: svgElement.textContent,
                                  fontFamily: style.fontFamily,
                                  fontSize: style.fontSize,
                                  fontWeight: style.fontWeight,
                                  fontStyle: style.fontStyle,
                                  fontVariant: style.fontVariant,
                                  whiteSpace: style.whiteSpace
                              };
                          }
                          else {
                              return {
                                  text: svgElement.textContent,
                                  fontFamily: "",
                                  fontSize: "0",
                              };
                          }
                      }
                      textMeasurementService.getSvgMeasurementProperties = getSvgMeasurementProperties;
                      /**
                       * This method returns the width of a div element.
                       * @param element The div element.
                       */
                      function getDivElementWidth(element) {
                          var style = getComputedStyle(element[0]);
                          if (style)
                              return style.width;
                          else
                              return "0";
                      }
                      textMeasurementService.getDivElementWidth = getDivElementWidth;
                      /**
                       * Compares labels text size to the available size and renders ellipses when the available size is smaller.
                       * @param textProperties The text properties (including text content) to use for text measurement.
                       * @param maxWidth The maximum width available for rendering the text.
                       */
                      function getTailoredTextOrDefault(textProperties, maxWidth) {
                          ensureDOM();
                          var strLength = textProperties.text.length;
                          if (strLength === 0) {
                              return textProperties.text;
                          }
                          var width = measureSvgTextWidth(textProperties);
                          if (width < maxWidth) {
                              return textProperties.text;
                          }
                          // Create a copy of the textProperties so we don't modify the one that's passed in.
                          var copiedTextProperties = Prototype.inherit(textProperties);
                          // Take the properties and apply them to svgTextElement
                          // Then, do the binary search to figure out the substring we want
                          // Set the substring on textElement argument
                          var text = copiedTextProperties.text = ellipsis + copiedTextProperties.text;
                          var min = 1;
                          var max = text.length;
                          var i = ellipsis.length;
                          while (min <= max) {
                              // num | 0 prefered to Math.floor(num) for performance benefits
                              i = (min + max) / 2 | 0;
                              copiedTextProperties.text = text.substr(0, i);
                              width = measureSvgTextWidth(copiedTextProperties);
                              if (maxWidth > width) {
                                  min = i + 1;
                              }
                              else if (maxWidth < width) {
                                  max = i - 1;
                              }
                              else {
                                  break;
                              }
                          }
                          // Since the search algorithm almost never finds an exact match,
                          // it will pick one of the closest two, which could result in a
                          // value bigger with than 'maxWidth' thus we need to go back by
                          // one to guarantee a smaller width than 'maxWidth'.
                          copiedTextProperties.text = text.substr(0, i);
                          width = measureSvgTextWidth(copiedTextProperties);
                          if (width > maxWidth) {
                              i--;
                          }
                          return text.substr(ellipsis.length, i - ellipsis.length) + ellipsis;
                      }
                      textMeasurementService.getTailoredTextOrDefault = getTailoredTextOrDefault;
                      /**
                       * Compares labels text size to the available size and renders ellipses when the available size is smaller.
                       * @param textElement The SVGTextElement containing the text to render.
                       * @param maxWidth The maximum width available for rendering the text.
                       */
                      function svgEllipsis(textElement, maxWidth) {
                          var properties = getSvgMeasurementProperties(textElement);
                          var originalText = properties.text;
                          var tailoredText = getTailoredTextOrDefault(properties, maxWidth);
                          if (originalText !== tailoredText) {
                              textElement.textContent = tailoredText;
                          }
                      }
                      textMeasurementService.svgEllipsis = svgEllipsis;
                      /**
                       * Word break textContent of <text> SVG element into <tspan>s
                       * Each tspan will be the height of a single line of text
                       * @param textElement - the SVGTextElement containing the text to wrap
                       * @param maxWidth - the maximum width available
                       * @param maxHeight - the maximum height available (defaults to single line)
                       * @param linePadding - (optional) padding to add to line height
                       */
                      function wordBreak(textElement, maxWidth, maxHeight, linePadding) {
                          if (linePadding === void 0) { linePadding = 0; }
                          var properties = getSvgMeasurementProperties(textElement);
                          var height = estimateSvgTextHeight(properties) + linePadding;
                          var maxNumLines = Math.max(1, Math.floor(maxHeight / height));
                          var node = d3.select(textElement);
                          // Save y of parent textElement to apply as first tspan dy
                          var firstDY = node.attr("y");
                          // Store and clear text content
                          var labelText = textElement.textContent;
                          textElement.textContent = null;
                          // Append a tspan for each word broken section
                          var words = wordBreaker.splitByWidth(labelText, properties, measureSvgTextWidth, maxWidth, maxNumLines);
                          for (var i = 0, ilen = words.length; i < ilen; i++) {
                              properties.text = words[i];
                              node
                                  .append("tspan")
                                  .attr({
                                  "x": 0,
                                  "dy": i === 0 ? firstDY : height,
                              })
                                  .text(getTailoredTextOrDefault(properties, maxWidth));
                          }
                      }
                      textMeasurementService.wordBreak = wordBreak;
                      /**
                       * Word break textContent of span element into <span>s
                       * Each span will be the height of a single line of text
                       * @param textElement - the element containing the text to wrap
                       * @param maxWidth - the maximum width available
                       * @param maxHeight - the maximum height available (defaults to single line)
                       * @param linePadding - (optional) padding to add to line height
                       */
                      function wordBreakOverflowingText(textElement, maxWidth, maxHeight, linePadding) {
                          if (linePadding === void 0) { linePadding = 0; }
                          var properties = getSvgMeasurementProperties(textElement);
                          var height = estimateSvgTextHeight(properties) + linePadding;
                          var maxNumLines = Math.max(1, Math.floor(maxHeight / height));
                          // Store and clear text content
                          var labelText = textElement.textContent;
                          textElement.textContent = null;
                          // Append a span for each word broken section
                          var words = wordBreaker.splitByWidth(labelText, properties, measureSvgTextWidth, maxWidth, maxNumLines);
                          var spanItem = d3.select(textElement)
                              .selectAll(OverflowingText.selector)
                              .data(words, function (d) { return $.inArray(d, words).toString(); });
                          spanItem
                              .enter()
                              .append("span")
                              .classed(OverflowingText.class, true)
                              .text(function (d) { return d; })
                              .style("width", PixelConverter.toString(maxWidth));
                      }
                      textMeasurementService.wordBreakOverflowingText = wordBreakOverflowingText;
                  })(textMeasurementService = formatting.textMeasurementService || (formatting.textMeasurementService = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  /** dateUtils module provides DateTimeSequence with set of additional date manipulation routines */
                  var dateUtils;
                  (function (dateUtils) {
                      var MonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
                      var MonthDaysLeap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
                      /**
                       * Returns bool indicating weither the provided year is a leap year.
                       * @param year - year value
                       */
                      function isLeap(year) {
                          return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
                      }
                      /**
                       * Returns number of days in the provided year/month.
                       * @param year - year value
                       * @param month - month value
                       */
                      function getMonthDays(year, month) {
                          return isLeap(year) ? MonthDaysLeap[month] : MonthDays[month];
                      }
                      /**
                       * Adds a specified number of years to the provided date.
                       * @param date - date value
                       * @param yearDelta - number of years to add
                       */
                      function addYears(date, yearDelta) {
                          var year = date.getFullYear();
                          var month = date.getMonth();
                          var day = date.getDate();
                          var isLeapDay = month === 2 && day === 29;
                          var result = new Date(date.getTime());
                          year = year + yearDelta;
                          if (isLeapDay && !isLeap(year)) {
                              day = 28;
                          }
                          result.setFullYear(year, month, day);
                          return result;
                      }
                      dateUtils.addYears = addYears;
                      /**
                       * Adds a specified number of months to the provided date.
                       * @param date - date value
                       * @param monthDelta - number of months to add
                       */
                      function addMonths(date, monthDelta) {
                          var year = date.getFullYear();
                          var month = date.getMonth();
                          var day = date.getDate();
                          var result = new Date(date.getTime());
                          year += (monthDelta - (monthDelta % 12)) / 12;
                          month += monthDelta % 12;
                          // VSTS 1325771: Certain column charts don't display any data
                          // Wrap arround the month if is after december (value 11)
                          if (month > 11) {
                              month = month % 12;
                              year++;
                          }
                          day = Math.min(day, getMonthDays(year, month));
                          result.setFullYear(year, month, day);
                          return result;
                      }
                      dateUtils.addMonths = addMonths;
                      /**
                       * Adds a specified number of weeks to the provided date.
                       * @param date - date value
                       * @param weeks - number of weeks to add
                       */
                      function addWeeks(date, weeks) {
                          return addDays(date, weeks * 7);
                      }
                      dateUtils.addWeeks = addWeeks;
                      /**
                       * Adds a specified number of days to the provided date.
                       * @param date - date value
                       * @param days - number of days to add
                       */
                      function addDays(date, days) {
                          var year = date.getFullYear();
                          var month = date.getMonth();
                          var day = date.getDate();
                          var result = new Date(date.getTime());
                          result.setFullYear(year, month, day + days);
                          return result;
                      }
                      dateUtils.addDays = addDays;
                      /**
                       * Adds a specified number of hours to the provided date.
                       * @param date - date value
                       * @param hours - number of hours to add
                       */
                      function addHours(date, hours) {
                          return new Date(date.getTime() + hours * 3600000);
                      }
                      dateUtils.addHours = addHours;
                      /**
                       * Adds a specified number of minutes to the provided date.
                       * @param date - date value
                       * @param minutes - number of minutes to add
                       */
                      function addMinutes(date, minutes) {
                          return new Date(date.getTime() + minutes * 60000);
                      }
                      dateUtils.addMinutes = addMinutes;
                      /**
                       * Adds a specified number of seconds to the provided date.
                       * @param date - date value
                       * @param seconds - number of seconds to add
                       */
                      function addSeconds(date, seconds) {
                          return new Date(date.getTime() + seconds * 1000);
                      }
                      dateUtils.addSeconds = addSeconds;
                      /**
                       * Adds a specified number of milliseconds to the provided date.
                       * @param date - date value
                       * @param milliseconds - number of milliseconds to add
                       */
                      function addMilliseconds(date, milliseconds) {
                          return new Date(date.getTime() + milliseconds);
                      }
                      dateUtils.addMilliseconds = addMilliseconds;
                  })(dateUtils = formatting.dateUtils || (formatting.dateUtils = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  // powerbi.extensibility.utils.type
                  var NumericSequenceRange = powerbi.extensibility.utils.type.NumericSequenceRange;
                  var NumericSequence = powerbi.extensibility.utils.type.NumericSequence;
                  var Double = powerbi.extensibility.utils.type.Double;
                  // powerbi.extensibility.utils.formatting
                  var DateTimeUnit = powerbi.extensibility.utils.formatting.DateTimeUnit;
                  /** Repreasents the sequence of the dates/times */
                  var DateTimeSequence = (function () {
                      // Constructors
                      /** Creates new instance of the DateTimeSequence */
                      function DateTimeSequence(unit) {
                          this.unit = unit;
                          this.sequence = [];
                          this.min = new Date("9999-12-31T23:59:59.999");
                          this.max = new Date("0001-01-01T00:00:00.000");
                      }
                      // Methods
                      /**
                       * Add a new Date to a sequence.
                       * @param date - date to add
                       */
                      DateTimeSequence.prototype.add = function (date) {
                          if (date < this.min) {
                              this.min = date;
                          }
                          if (date > this.max) {
                              this.max = date;
                          }
                          this.sequence.push(date);
                      };
                      // Methods
                      /**
                       * Extends the sequence to cover new date range
                       * @param min - new min to be covered by sequence
                       * @param max - new max to be covered by sequence
                       */
                      DateTimeSequence.prototype.extendToCover = function (min, max) {
                          var x = this.min;
                          while (min < x) {
                              x = DateTimeSequence.addInterval(x, -this.interval, this.unit);
                              this.sequence.splice(0, 0, x);
                          }
                          this.min = x;
                          x = this.max;
                          while (x < max) {
                              x = DateTimeSequence.addInterval(x, this.interval, this.unit);
                              this.sequence.push(x);
                          }
                          this.max = x;
                      };
                      /**
                       * Move the sequence to cover new date range
                       * @param min - new min to be covered by sequence
                       * @param max - new max to be covered by sequence
                       */
                      DateTimeSequence.prototype.moveToCover = function (min, max) {
                          var delta = DateTimeSequence.getDelta(min, max, this.unit);
                          var count = Math.floor(delta / this.interval);
                          this.min = DateTimeSequence.addInterval(this.min, count * this.interval, this.unit);
                          this.sequence = [];
                          this.sequence.push(this.min);
                          this.max = this.min;
                          while (this.max < max) {
                              this.max = DateTimeSequence.addInterval(this.max, this.interval, this.unit);
                              this.sequence.push(this.max);
                          }
                      };
                      // Static
                      /**
                       * Calculate a new DateTimeSequence
                       * @param dataMin - Date representing min of the data range
                       * @param dataMax - Date representing max of the data range
                       * @param expectedCount - expected number of intervals in the sequence
                       * @param unit - of the intervals in the sequence
                       */
                      DateTimeSequence.calculate = function (dataMin, dataMax, expectedCount, unit) {
                          if (!unit) {
                              unit = DateTimeSequence.getIntervalUnit(dataMin, dataMax, expectedCount);
                          }
                          switch (unit) {
                              case DateTimeUnit.Year:
                                  return DateTimeSequence.calculateYears(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Month:
                                  return DateTimeSequence.calculateMonths(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Week:
                                  return DateTimeSequence.calculateWeeks(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Day:
                                  return DateTimeSequence.calculateDays(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Hour:
                                  return DateTimeSequence.calculateHours(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Minute:
                                  return DateTimeSequence.calculateMinutes(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Second:
                                  return DateTimeSequence.calculateSeconds(dataMin, dataMax, expectedCount);
                              case DateTimeUnit.Millisecond:
                                  return DateTimeSequence.calculateMilliseconds(dataMin, dataMax, expectedCount);
                          }
                      };
                      DateTimeSequence.calculateYears = function (dataMin, dataMax, expectedCount) {
                          // Calculate range and sequence
                          var yearsRange = NumericSequenceRange.calculateDataRange(dataMin.getFullYear(), dataMax.getFullYear(), false);
                          // Calculate year sequence
                          var sequence = NumericSequence.calculate(NumericSequenceRange.calculate(0, yearsRange.max - yearsRange.min), expectedCount, 0, null, null, [1, 2, 5]);
                          var newMinYear = Math.floor(yearsRange.min / sequence.interval) * sequence.interval;
                          var date = new Date(newMinYear, 0, 1);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Year);
                          return result;
                      };
                      DateTimeSequence.calculateMonths = function (dataMin, dataMax, expectedCount) {
                          // Calculate range
                          var minYear = dataMin.getFullYear();
                          var maxYear = dataMax.getFullYear();
                          var minMonth = dataMin.getMonth();
                          var maxMonth = (maxYear - minYear) * 12 + dataMax.getMonth();
                          var date = new Date(minYear, 0, 1);
                          // Calculate month sequence
                          var sequence = NumericSequence.calculateUnits(minMonth, maxMonth, expectedCount, [1, 2, 3, 6, 12]);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Month);
                          return result;
                      };
                      DateTimeSequence.calculateWeeks = function (dataMin, dataMax, expectedCount) {
                          var firstDayOfWeek = 0;
                          var minDayOfWeek = dataMin.getDay();
                          var dayOffset = (minDayOfWeek - firstDayOfWeek + 7) % 7;
                          var minDay = dataMin.getDate() - dayOffset;
                          // Calculate range
                          var date = new Date(dataMin.getFullYear(), dataMin.getMonth(), minDay);
                          var min = 0;
                          var max = Double.ceilWithPrecision(DateTimeSequence.getDelta(date, dataMax, DateTimeUnit.Week));
                          // Calculate week sequence
                          var sequence = NumericSequence.calculateUnits(min, max, expectedCount, [1, 2, 4, 8]);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Week);
                          return result;
                      };
                      DateTimeSequence.calculateDays = function (dataMin, dataMax, expectedCount) {
                          // Calculate range
                          var date = new Date(dataMin.getFullYear(), dataMin.getMonth(), dataMin.getDate());
                          var min = 0;
                          var max = Double.ceilWithPrecision(DateTimeSequence.getDelta(dataMin, dataMax, DateTimeUnit.Day));
                          // Calculate day sequence
                          var sequence = NumericSequence.calculateUnits(min, max, expectedCount, [1, 2, 7, 14]);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Day);
                          return result;
                      };
                      DateTimeSequence.calculateHours = function (dataMin, dataMax, expectedCount) {
                          // Calculate range
                          var date = new Date(dataMin.getFullYear(), dataMin.getMonth(), dataMin.getDate());
                          var min = Double.floorWithPrecision(DateTimeSequence.getDelta(date, dataMin, DateTimeUnit.Hour));
                          var max = Double.ceilWithPrecision(DateTimeSequence.getDelta(date, dataMax, DateTimeUnit.Hour));
                          // Calculate hour sequence
                          var sequence = NumericSequence.calculateUnits(min, max, expectedCount, [1, 2, 3, 6, 12, 24]);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Hour);
                          return result;
                      };
                      DateTimeSequence.calculateMinutes = function (dataMin, dataMax, expectedCount) {
                          // Calculate range
                          var date = new Date(dataMin.getFullYear(), dataMin.getMonth(), dataMin.getDate(), dataMin.getHours());
                          var min = Double.floorWithPrecision(DateTimeSequence.getDelta(date, dataMin, DateTimeUnit.Minute));
                          var max = Double.ceilWithPrecision(DateTimeSequence.getDelta(date, dataMax, DateTimeUnit.Minute));
                          // Calculate minutes numeric sequence
                          var sequence = NumericSequence.calculateUnits(min, max, expectedCount, [1, 2, 5, 10, 15, 30, 60, 60 * 2, 60 * 3, 60 * 6, 60 * 12, 60 * 24]);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Minute);
                          return result;
                      };
                      DateTimeSequence.calculateSeconds = function (dataMin, dataMax, expectedCount) {
                          // Calculate range
                          var date = new Date(dataMin.getFullYear(), dataMin.getMonth(), dataMin.getDate(), dataMin.getHours(), dataMin.getMinutes());
                          var min = Double.floorWithPrecision(DateTimeSequence.getDelta(date, dataMin, DateTimeUnit.Second));
                          var max = Double.ceilWithPrecision(DateTimeSequence.getDelta(date, dataMax, DateTimeUnit.Second));
                          // Calculate minutes numeric sequence
                          var sequence = NumericSequence.calculateUnits(min, max, expectedCount, [1, 2, 5, 10, 15, 30, 60, 60 * 2, 60 * 5, 60 * 10, 60 * 15, 60 * 30, 60 * 60]);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Second);
                          return result;
                      };
                      DateTimeSequence.calculateMilliseconds = function (dataMin, dataMax, expectedCount) {
                          // Calculate range
                          var date = new Date(dataMin.getFullYear(), dataMin.getMonth(), dataMin.getDate(), dataMin.getHours(), dataMin.getMinutes(), dataMin.getSeconds());
                          var min = DateTimeSequence.getDelta(date, dataMin, DateTimeUnit.Millisecond);
                          var max = DateTimeSequence.getDelta(date, dataMax, DateTimeUnit.Millisecond);
                          // Calculate milliseconds numeric sequence
                          var sequence = NumericSequence.calculate(NumericSequenceRange.calculate(min, max), expectedCount, 0);
                          // Convert to date sequence
                          var result = DateTimeSequence.fromNumericSequence(date, sequence, DateTimeUnit.Millisecond);
                          return result;
                      };
                      DateTimeSequence.addInterval = function (value, interval, unit) {
                          interval = Math.round(interval);
                          switch (unit) {
                              case DateTimeUnit.Year:
                                  return formatting.dateUtils.addYears(value, interval);
                              case DateTimeUnit.Month:
                                  return formatting.dateUtils.addMonths(value, interval);
                              case DateTimeUnit.Week:
                                  return formatting.dateUtils.addWeeks(value, interval);
                              case DateTimeUnit.Day:
                                  return formatting.dateUtils.addDays(value, interval);
                              case DateTimeUnit.Hour:
                                  return formatting.dateUtils.addHours(value, interval);
                              case DateTimeUnit.Minute:
                                  return formatting.dateUtils.addMinutes(value, interval);
                              case DateTimeUnit.Second:
                                  return formatting.dateUtils.addSeconds(value, interval);
                              case DateTimeUnit.Millisecond:
                                  return formatting.dateUtils.addMilliseconds(value, interval);
                          }
                      };
                      DateTimeSequence.fromNumericSequence = function (date, sequence, unit) {
                          var result = new DateTimeSequence(unit);
                          for (var i = 0; i < sequence.sequence.length; i++) {
                              var x = sequence.sequence[i];
                              var d = DateTimeSequence.addInterval(date, x, unit);
                              result.add(d);
                          }
                          result.interval = sequence.interval;
                          result.intervalOffset = sequence.intervalOffset;
                          return result;
                      };
                      DateTimeSequence.getDelta = function (min, max, unit) {
                          var delta = 0;
                          switch (unit) {
                              case DateTimeUnit.Year:
                                  delta = max.getFullYear() - min.getFullYear();
                                  break;
                              case DateTimeUnit.Month:
                                  delta = (max.getFullYear() - min.getFullYear()) * 12 + max.getMonth() - min.getMonth();
                                  break;
                              case DateTimeUnit.Week:
                                  delta = (max.getTime() - min.getTime()) / (7 * 24 * 3600000);
                                  break;
                              case DateTimeUnit.Day:
                                  delta = (max.getTime() - min.getTime()) / (24 * 3600000);
                                  break;
                              case DateTimeUnit.Hour:
                                  delta = (max.getTime() - min.getTime()) / 3600000;
                                  break;
                              case DateTimeUnit.Minute:
                                  delta = (max.getTime() - min.getTime()) / 60000;
                                  break;
                              case DateTimeUnit.Second:
                                  delta = (max.getTime() - min.getTime()) / 1000;
                                  break;
                              case DateTimeUnit.Millisecond:
                                  delta = max.getTime() - min.getTime();
                                  break;
                          }
                          return delta;
                      };
                      DateTimeSequence.getIntervalUnit = function (min, max, maxCount) {
                          maxCount = Math.max(maxCount, 2);
                          var totalDays = DateTimeSequence.getDelta(min, max, DateTimeUnit.Day);
                          if (totalDays > 356 && totalDays >= 30 * 6 * maxCount)
                              return DateTimeUnit.Year;
                          if (totalDays > 60 && totalDays > 7 * maxCount)
                              return DateTimeUnit.Month;
                          if (totalDays > 14 && totalDays > 2 * maxCount)
                              return DateTimeUnit.Week;
                          var totalHours = DateTimeSequence.getDelta(min, max, DateTimeUnit.Hour);
                          if (totalDays > 2 && totalHours > 12 * maxCount)
                              return DateTimeUnit.Day;
                          if (totalHours >= 24 && totalHours >= maxCount)
                              return DateTimeUnit.Hour;
                          var totalMinutes = DateTimeSequence.getDelta(min, max, DateTimeUnit.Minute);
                          if (totalMinutes > 2 && totalMinutes >= maxCount)
                              return DateTimeUnit.Minute;
                          var totalSeconds = DateTimeSequence.getDelta(min, max, DateTimeUnit.Second);
                          if (totalSeconds > 2 && totalSeconds >= 0.8 * maxCount)
                              return DateTimeUnit.Second;
                          var totalMilliseconds = DateTimeSequence.getDelta(min, max, DateTimeUnit.Millisecond);
                          if (totalMilliseconds > 0)
                              return DateTimeUnit.Millisecond;
                          // If the size of the range is 0 we need to guess the unit based on the date's non-zero values starting with milliseconds
                          var date = min;
                          if (date.getMilliseconds() !== 0)
                              return DateTimeUnit.Millisecond;
                          if (date.getSeconds() !== 0)
                              return DateTimeUnit.Second;
                          if (date.getMinutes() !== 0)
                              return DateTimeUnit.Minute;
                          if (date.getHours() !== 0)
                              return DateTimeUnit.Hour;
                          if (date.getDate() !== 1)
                              return DateTimeUnit.Day;
                          if (date.getMonth() !== 0)
                              return DateTimeUnit.Month;
                          return DateTimeUnit.Year;
                      };
                      // Constants
                      DateTimeSequence.MIN_COUNT = 1;
                      DateTimeSequence.MAX_COUNT = 1000;
                      return DateTimeSequence;
                  }());
                  formatting.DateTimeSequence = DateTimeSequence;
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var regexCache;
                  /**
                   * Translate .NET format into something supported by jQuery.Globalize.
                   */
                  function findDateFormat(value, format, cultureName) {
                      switch (format) {
                          case "m":
                              // Month + day
                              format = "M";
                              break;
                          case "O":
                          case "o":
                              // Roundtrip
                              format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'0000'";
                              break;
                          case "R":
                          case "r":
                              // RFC1123 pattern - - time must be converted to UTC before formatting 
                              value = new Date(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate(), value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds());
                              format = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
                              break;
                          case "s":
                              // Sortable - should use invariant culture
                              format = "S";
                              break;
                          case "u":
                              // Universal sortable - should convert to UTC before applying the "yyyy'-'MM'-'dd HH':'mm':'ss'Z' format.
                              value = new Date(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate(), value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds());
                              format = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
                              break;
                          case "U":
                              // Universal full - the pattern is same as F but the time must be converted to UTC before formatting
                              value = new Date(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate(), value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds());
                              format = "F";
                              break;
                          case "y":
                          case "Y":
                              // Year and month
                              switch (cultureName) {
                                  case "default":
                                  case "en":
                                  case "en-US":
                                      format = "MMMM, yyyy"; // Fix the default year-month pattern for english
                                      break;
                                  default:
                                      format = "Y"; // For other cultures - use the localized pattern
                              }
                              break;
                      }
                      return { value: value, format: format };
                  }
                  formatting.findDateFormat = findDateFormat;
                  /**
                   * Translates unsupported .NET custom format expressions to the custom expressions supported by JQuery.Globalize.
                   */
                  function fixDateTimeFormat(format) {
                      // Fix for the "K" format (timezone): 
                      // T he js dates don't have a kind property so we'll support only local kind which is equavalent to zzz format.
                      format = format.replace(/%K/g, "zzz");
                      format = format.replace(/K/g, "zzz");
                      format = format.replace(/fffffff/g, "fff0000");
                      format = format.replace(/ffffff/g, "fff000");
                      format = format.replace(/fffff/g, "fff00");
                      format = format.replace(/ffff/g, "fff0");
                      // Fix for the 5 digit year: "yyyyy" format. 
                      // The Globalize doesn't support dates greater than 9999 so we replace the "yyyyy" with "0yyyy".
                      format = format.replace(/yyyyy/g, "0yyyy");
                      // Fix for the 3 digit year: "yyy" format. 
                      // The Globalize doesn't support this formatting so we need to replace it with the 4 digit year "yyyy" format.
                      format = format.replace(/(^y|^)yyy(^y|$)/g, "yyyy");
                      if (!regexCache) {
                          // Creating Regexes for cases "Using single format specifier" 
                          // - http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx#UsingSingleSpecifiers
                          // This is not supported from The Globalize.
                          // The case covers all single "%" lead specifier (like "%d" but not %dd) 
                          // The cases as single "%d" are filtered in if the bellow.
                          // (?!S) where S is the specifier make sure that we only one symbol for specifier.
                          regexCache = ["d", "f", "F", "g", "h", "H", "K", "m", "M", "s", "t", "y", "z", ":", "/"].map(function (s) {
                              return { r: new RegExp("\%" + s + "(?!" + s + ")", "g"), s: s };
                          });
                      }
                      if (format.indexOf("%") !== -1 && format.length > 2) {
                          for (var i = 0; i < regexCache.length; i++) {
                              format = format.replace(regexCache[i].r, regexCache[i].s);
                          }
                      }
                      return format;
                  }
                  formatting.fixDateTimeFormat = fixDateTimeFormat;
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var font;
                  (function (font_1) {
                      var FamilyInfo = (function () {
                          function FamilyInfo(families) {
                              this.families = families;
                          }
                          ;
                          Object.defineProperty(FamilyInfo.prototype, "family", {
                              /**
                               * Gets the first font "wf_" font family since it will always be loaded.
                               */
                              get: function () {
                                  return this.getFamily();
                              },
                              enumerable: true,
                              configurable: true
                          });
                          /**
                          * Gets the first font family that matches regex (if provided).
                          * Default regex looks for "wf_" fonts which are always loaded.
                          */
                          FamilyInfo.prototype.getFamily = function (regex) {
                              if (regex === void 0) { regex = /^wf_/; }
                              if (!this.families) {
                                  return null;
                              }
                              return regex ? _.find(this.families, function (fontFamily) { return regex.test(fontFamily); }) : this.families[0];
                          };
                          Object.defineProperty(FamilyInfo.prototype, "css", {
                              /**
                               * Gets the CSS string for the "font-family" CSS attribute.
                               */
                              get: function () {
                                  return this.getCSS();
                              },
                              enumerable: true,
                              configurable: true
                          });
                          /**
                           * Gets the CSS string for the "font-family" CSS attribute.
                           */
                          FamilyInfo.prototype.getCSS = function () {
                              return this.families ? this.families.map((function (font) { return font.indexOf(" ") > 0 ? "'" + font + "'" : font; })).join(", ") : null;
                          };
                          return FamilyInfo;
                      }());
                      font_1.FamilyInfo = FamilyInfo;
                  })(font = formatting.font || (formatting.font = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var font;
                  (function (font) {
                      font.fallbackFonts = ["helvetica", "arial", "sans-serif"];
                      font.Family = {
                          light: new font.FamilyInfo(font.fallbackFonts),
                          semilight: new font.FamilyInfo(font.fallbackFonts),
                          regular: new font.FamilyInfo(font.fallbackFonts),
                          semibold: new font.FamilyInfo(font.fallbackFonts),
                          bold: new font.FamilyInfo(font.fallbackFonts),
                          lightSecondary: new font.FamilyInfo(font.fallbackFonts),
                          regularSecondary: new font.FamilyInfo(font.fallbackFonts),
                          boldSecondary: new font.FamilyInfo(font.fallbackFonts)
                      };
                  })(font = formatting.font || (formatting.font = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  // powerbi.extensibility.utils.type
                  var Double = powerbi.extensibility.utils.type.Double;
                  var RegExpExtensions = powerbi.extensibility.utils.type.RegExpExtensions;
                  // powerbi.extensibility.utils.formatting
                  var stringExtensions = powerbi.extensibility.utils.formatting.stringExtensions;
                  var findDateFormat = powerbi.extensibility.utils.formatting.findDateFormat;
                  var fixDateTimeFormat = powerbi.extensibility.utils.formatting.fixDateTimeFormat;
                  var DateTimeUnit = powerbi.extensibility.utils.formatting.DateTimeUnit;
                  /** Formatting Encoder */
                  var formattingEncoder;
                  (function (formattingEncoder) {
                      // quoted and escaped literal patterns
                      // NOTE: the final three cases match .NET behavior
                      var literalPatterns = [
                          "'[^']*'",
                          "\"[^\"]*\"",
                          "\\\\.",
                          "'[^']*$",
                          "\"[^\"]*$",
                          "\\\\$",
                      ];
                      var literalMatcher = new RegExp(literalPatterns.join("|"), "g");
                      // Unicode U+E000 - U+F8FF is a private area and so we can use the chars from the range to encode the escaped sequences
                      function removeLiterals(format) {
                          literalMatcher.lastIndex = 0;
                          // just in case consecutive non-literals have some meaning
                          return format.replace(literalMatcher, "\uE100");
                      }
                      formattingEncoder.removeLiterals = removeLiterals;
                      function preserveLiterals(format, literals) {
                          literalMatcher.lastIndex = 0;
                          for (;;) {
                              var match = literalMatcher.exec(format);
                              if (!match)
                                  break;
                              var literal = match[0];
                              var literalOffset = literalMatcher.lastIndex - literal.length;
                              var token = String.fromCharCode(0xE100 + literals.length);
                              literals.push(literal);
                              format = format.substr(0, literalOffset) + token + format.substr(literalMatcher.lastIndex);
                              // back to avoid skipping due to removed literal substring
                              literalMatcher.lastIndex = literalOffset + 1;
                          }
                          return format;
                      }
                      formattingEncoder.preserveLiterals = preserveLiterals;
                      function restoreLiterals(format, literals, quoted) {
                          if (quoted === void 0) { quoted = true; }
                          var count = literals.length;
                          for (var i = 0; i < count; i++) {
                              var token = String.fromCharCode(0xE100 + i);
                              var literal = literals[i];
                              if (!quoted) {
                                  // caller wants literals to be re-inserted without escaping
                                  var firstChar = literal[0];
                                  if (firstChar === "\\" || literal.length === 1 || literal[literal.length - 1] !== firstChar) {
                                      // either escaped literal OR quoted literal that's missing the trailing quote
                                      // in either case we only remove the leading character
                                      literal = literal.substring(1);
                                  }
                                  else {
                                      // so must be a quoted literal with both starting and ending quote
                                      literal = literal.substring(1, literal.length - 1);
                                  }
                              }
                              format = format.replace(token, literal);
                          }
                          return format;
                      }
                      formattingEncoder.restoreLiterals = restoreLiterals;
                  })(formattingEncoder || (formattingEncoder = {}));
                  var IndexedTokensRegex = /({{)|(}})|{(\d+[^}]*)}/g;
                  var ZeroPlaceholder = "0";
                  var DigitPlaceholder = "#";
                  var ExponentialFormatChar = "E";
                  var NumericPlaceholders = [ZeroPlaceholder, DigitPlaceholder];
                  var NumericPlaceholderRegex = new RegExp(NumericPlaceholders.join("|"), "g");
                  /** Formatting Service */
                  var FormattingService = (function () {
                      function FormattingService() {
                      }
                      FormattingService.prototype.formatValue = function (value, format, cultureSelector) {
                          // Handle special cases
                          if (value === undefined || value === null) {
                              return "";
                          }
                          var gculture = this.getCulture(cultureSelector);
                          if (dateTimeFormat.canFormat(value)) {
                              // Dates
                              return dateTimeFormat.format(value, format, gculture);
                          }
                          else if (numberFormat.canFormat(value)) {
                              // Numbers
                              return numberFormat.format(value, format, gculture);
                          }
                          // Other data types - return as string
                          return value.toString();
                      };
                      FormattingService.prototype.format = function (formatWithIndexedTokens, args, culture) {
                          var _this = this;
                          if (!formatWithIndexedTokens) {
                              return "";
                          }
                          var result = formatWithIndexedTokens.replace(IndexedTokensRegex, function (match, left, right, argToken) {
                              if (left) {
                                  return "{";
                              }
                              else if (right) {
                                  return "}";
                              }
                              else {
                                  var parts = argToken.split(":");
                                  var argIndex = parseInt(parts[0], 10);
                                  var argFormat = parts[1];
                                  return _this.formatValue(args[argIndex], argFormat, culture);
                              }
                          });
                          return result;
                      };
                      FormattingService.prototype.isStandardNumberFormat = function (format) {
                          return numberFormat.isStandardFormat(format);
                      };
                      FormattingService.prototype.formatNumberWithCustomOverride = function (value, format, nonScientificOverrideFormat, culture) {
                          var gculture = this.getCulture(culture);
                          return numberFormat.formatWithCustomOverride(value, format, nonScientificOverrideFormat, gculture);
                      };
                      FormattingService.prototype.dateFormatString = function (unit) {
                          if (!this._dateTimeScaleFormatInfo)
                              this.initialize();
                          return this._dateTimeScaleFormatInfo.getFormatString(unit);
                      };
                      /**
                       * Sets the current localization culture
                       * @param cultureSelector - name of a culture: "en", "en-UK", "fr-FR" etc. (See National Language Support (NLS) for full lists. Use "default" for invariant culture).
                       */
                      FormattingService.prototype.setCurrentCulture = function (cultureSelector) {
                          if (this._currentCultureSelector !== cultureSelector) {
                              this._currentCulture = this.getCulture(cultureSelector);
                              this._currentCultureSelector = cultureSelector;
                              this._dateTimeScaleFormatInfo = new DateTimeScaleFormatInfo(this._currentCulture);
                          }
                      };
                      /**
                       * Gets the culture assotiated with the specified cultureSelector ("en", "en-US", "fr-FR" etc).
                       * @param cultureSelector - name of a culture: "en", "en-UK", "fr-FR" etc. (See National Language Support (NLS) for full lists. Use "default" for invariant culture).
                       * Exposing this function for testability of unsupported cultures
                       */
                      FormattingService.prototype.getCulture = function (cultureSelector) {
                          if (cultureSelector == null) {
                              if (this._currentCulture == null) {
                                  this.initialize();
                              }
                              return this._currentCulture;
                          }
                          else {
                              var culture = Globalize.findClosestCulture(cultureSelector);
                              if (!culture)
                                  culture = Globalize.culture("en-US");
                              return culture;
                          }
                      };
                      /** By default the Globalization module initializes to the culture/calendar provided in the language/culture URL params */
                      FormattingService.prototype.initialize = function () {
                          var cultureName = this.getCurrentCulture();
                          this.setCurrentCulture(cultureName);
                          var calendarName = this.getUrlParam("calendar");
                          if (calendarName) {
                              var culture = this._currentCulture;
                              var c = culture.calendars[calendarName];
                              if (c) {
                                  culture.calendar = c;
                              }
                          }
                      };
                      /**
                       *  Exposing this function for testability
                       */
                      FormattingService.prototype.getCurrentCulture = function () {
                          var urlParam = this.getUrlParam("language");
                          if (urlParam) {
                              return urlParam;
                          }
                          if (powerbi && powerbi.common && powerbi.common.cultureInfo) {
                              // Get cultureInfo set in powerbi
                              return powerbi.common.cultureInfo;
                          }
                          return window.navigator.userLanguage || window.navigator["language"] || Globalize.culture().name;
                      };
                      /**
                       *  Exposing this function for testability
                       *  @param name: queryString name
                       */
                      FormattingService.prototype.getUrlParam = function (name) {
                          var param = window.location.search.match(RegExp("[?&]" + name + "=([^&]*)"));
                          return param ? param[1] : undefined;
                      };
                      return FormattingService;
                  }());
                  formatting.FormattingService = FormattingService;
                  /**
                   * DateTimeFormat module contains the static methods for formatting the DateTimes.
                   * It extends the JQuery.Globalize functionality to support complete set of .NET
                   * formatting expressions for dates.
                   */
                  var dateTimeFormat;
                  (function (dateTimeFormat) {
                      var _currentCachedFormat;
                      var _currentCachedProcessedFormat;
                      /** Evaluates if the value can be formatted using the NumberFormat */
                      function canFormat(value) {
                          var result = value instanceof Date;
                          return result;
                      }
                      dateTimeFormat.canFormat = canFormat;
                      /** Formats the date using provided format and culture */
                      function format(value, format, culture) {
                          format = format || "G";
                          var isStandard = format.length === 1;
                          try {
                              if (isStandard) {
                                  return formatDateStandard(value, format, culture);
                              }
                              else {
                                  return formatDateCustom(value, format, culture);
                              }
                          }
                          catch (e) {
                              return formatDateStandard(value, "G", culture);
                          }
                      }
                      dateTimeFormat.format = format;
                      /** Formats the date using standard format expression */
                      function formatDateStandard(value, format, culture) {
                          // In order to provide parity with .NET we have to support additional set of DateTime patterns.
                          var patterns = culture.calendar.patterns;
                          // Extend supported set of patterns
                          ensurePatterns(culture.calendar);
                          // Handle extended set of formats
                          var output = findDateFormat(value, format, culture.name);
                          if (output.format.length === 1)
                              format = patterns[output.format];
                          else
                              format = output.format;
                          // need to revisit when globalization is enabled
                          culture = Globalize.culture("en-US");
                          return Globalize.format(output.value, format, culture);
                      }
                      /** Formats the date using custom format expression */
                      function formatDateCustom(value, format, culture) {
                          var result;
                          var literals = [];
                          format = formattingEncoder.preserveLiterals(format, literals);
                          if (format.indexOf("F") > -1) {
                              // F is not supported so we need to replace the F with f based on the milliseconds
                              // Replace all sequences of F longer than 3 with "FFF"
                              format = stringExtensions.replaceAll(format, "FFFF", "FFF");
                              // Based on milliseconds update the format to use fff
                              var milliseconds = value.getMilliseconds();
                              if (milliseconds % 10 >= 1) {
                                  format = stringExtensions.replaceAll(format, "FFF", "fff");
                              }
                              format = stringExtensions.replaceAll(format, "FFF", "FF");
                              if ((milliseconds % 100) / 10 >= 1) {
                                  format = stringExtensions.replaceAll(format, "FF", "ff");
                              }
                              format = stringExtensions.replaceAll(format, "FF", "F");
                              if ((milliseconds % 1000) / 100 >= 1) {
                                  format = stringExtensions.replaceAll(format, "F", "f");
                              }
                              format = stringExtensions.replaceAll(format, "F", "");
                              if (format === "" || format === "%")
                                  return "";
                          }
                          format = processCustomDateTimeFormat(format);
                          result = Globalize.format(value, format, culture);
                          result = localize(result, culture.calendar);
                          result = formattingEncoder.restoreLiterals(result, literals, false);
                          return result;
                      }
                      /** Translates unsupported .NET custom format expressions to the custom expressions supported by JQuery.Globalize */
                      function processCustomDateTimeFormat(format) {
                          if (format === _currentCachedFormat) {
                              return _currentCachedProcessedFormat;
                          }
                          _currentCachedFormat = format;
                          format = fixDateTimeFormat(format);
                          _currentCachedProcessedFormat = format;
                          return format;
                      }
                      /** Localizes the time separator symbol */
                      function localize(value, dictionary) {
                          var timeSeparator = dictionary[":"];
                          if (timeSeparator === ":") {
                              return value;
                          }
                          var result = "";
                          var count = value.length;
                          for (var i = 0; i < count; i++) {
                              var char = value.charAt(i);
                              switch (char) {
                                  case ":":
                                      result += timeSeparator;
                                      break;
                                  default:
                                      result += char;
                                      break;
                              }
                          }
                          return result;
                      }
                      function ensurePatterns(calendar) {
                          var patterns = calendar.patterns;
                          if (patterns["g"] === undefined) {
                              patterns["g"] = patterns["f"].replace(patterns["D"], patterns["d"]); // Generic: Short date, short time
                              patterns["G"] = patterns["F"].replace(patterns["D"], patterns["d"]); // Generic: Short date, long time
                          }
                      }
                  })(dateTimeFormat || (dateTimeFormat = {}));
                  /**
                   * NumberFormat module contains the static methods for formatting the numbers.
                   * It extends the JQuery.Globalize functionality to support complete set of .NET
                   * formatting expressions for numeric types including custom formats.
                   */
                  var numberFormat;
                  (function (numberFormat) {
                      var NonScientificFormatRegex = /^\{.+\}.*/;
                      var NumericalPlaceHolderRegex = /\{.+\}/;
                      var ScientificFormatRegex = /e[+-]*[0#]+/i;
                      var StandardFormatRegex = /^[a-z]\d{0,2}$/i; // a letter + up to 2 digits for precision specifier
                      var TrailingZerosRegex = /0+$/;
                      var DecimalFormatRegex = /\.([0#]*)/g;
                      var NumericFormatRegex = /[0#,\.]+[0,#]*/g;
                      // (?=...) is a positive lookahead assertion. The RE is asking for the last digit placeholder, [0#],
                      // which is followed by non-digit placeholders and the end of string, [^0#]*$. But it only matches
                      // the last digit placeholder, not anything that follows because the positive lookahead isn"t included
                      // in the match - it is only a condition.
                      var LastNumericPlaceholderRegex = /([0#])(?=[^0#]*$)/;
                      var DecimalFormatCharacter = ".";
                      numberFormat.NumberFormatComponentsDelimeter = ";";
                      function getNonScientificFormatWithPrecision(baseFormat, numericFormat) {
                          if (!numericFormat || baseFormat === undefined)
                              return baseFormat;
                          var newFormat = "{0:" + numericFormat + "}";
                          return baseFormat.replace("{0}", newFormat);
                      }
                      function getNumericFormat(value, baseFormat) {
                          if (baseFormat == null)
                              return baseFormat;
                          if (hasFormatComponents(baseFormat)) {
                              var _a = numberFormat.getComponents(baseFormat), positive = _a.positive, negative = _a.negative, zero = _a.zero;
                              if (value > 0)
                                  return getNumericFormatFromComponent(value, positive);
                              else if (value === 0)
                                  return getNumericFormatFromComponent(value, zero);
                              return getNumericFormatFromComponent(value, negative);
                          }
                          return getNumericFormatFromComponent(value, baseFormat);
                      }
                      numberFormat.getNumericFormat = getNumericFormat;
                      function getNumericFormatFromComponent(value, format) {
                          var match = RegExpExtensions.run(NumericFormatRegex, format);
                          if (match)
                              return match[0];
                          return format;
                      }
                      function addDecimalsToFormat(baseFormat, decimals, trailingZeros) {
                          if (decimals == null)
                              return baseFormat;
                          // Default format string
                          if (baseFormat == null)
                              baseFormat = ZeroPlaceholder;
                          if (hasFormatComponents(baseFormat)) {
                              var _a = numberFormat.getComponents(baseFormat), positive = _a.positive, negative = _a.negative, zero = _a.zero;
                              var formats = [positive, negative, zero];
                              for (var i = 0; i < formats.length; i++) {
                                  // Update format in formats array
                                  formats[i] = addDecimalsToFormatComponent(formats[i], decimals, trailingZeros);
                              }
                              return formats.join(numberFormat.NumberFormatComponentsDelimeter);
                          }
                          return addDecimalsToFormatComponent(baseFormat, decimals, trailingZeros);
                      }
                      numberFormat.addDecimalsToFormat = addDecimalsToFormat;
                      function addDecimalsToFormatComponent(format, decimals, trailingZeros) {
                          decimals = Math.abs(decimals);
                          if (decimals >= 0) {
                              var literals = [];
                              format = formattingEncoder.preserveLiterals(format, literals);
                              var placeholder = trailingZeros ? ZeroPlaceholder : DigitPlaceholder;
                              var decimalPlaceholders = stringExtensions.repeat(placeholder, Math.abs(decimals));
                              var match = RegExpExtensions.run(DecimalFormatRegex, format);
                              if (match) {
                                  var beforeDecimal = format.substr(0, match.index);
                                  var formatDecimal = format.substr(match.index + 1, match[1].length);
                                  var afterDecimal = format.substr(match.index + match[0].length);
                                  if (trailingZeros)
                                      // Use explicit decimals argument as placeholders
                                      formatDecimal = decimalPlaceholders;
                                  else {
                                      var decimalChange = decimalPlaceholders.length - formatDecimal.length;
                                      if (decimalChange > 0)
                                          // Append decimalPlaceholders to existing decimal portion of format string
                                          formatDecimal = formatDecimal + decimalPlaceholders.slice(-decimalChange);
                                      else if (decimalChange < 0)
                                          // Remove decimals from formatDecimal
                                          formatDecimal = formatDecimal.slice(0, decimalChange);
                                  }
                                  if (formatDecimal.length > 0)
                                      formatDecimal = DecimalFormatCharacter + formatDecimal;
                                  format = beforeDecimal + formatDecimal + afterDecimal;
                              }
                              else if (decimalPlaceholders.length > 0) {
                                  // Replace last numeric placeholder with decimal portion
                                  format = format.replace(LastNumericPlaceholderRegex, "$1" + DecimalFormatCharacter + decimalPlaceholders);
                              }
                              if (literals.length !== 0)
                                  format = formattingEncoder.restoreLiterals(format, literals);
                          }
                          return format;
                      }
                      function hasFormatComponents(format) {
                          return formattingEncoder.removeLiterals(format).indexOf(numberFormat.NumberFormatComponentsDelimeter) !== -1;
                      }
                      numberFormat.hasFormatComponents = hasFormatComponents;
                      function getComponents(format) {
                          var signFormat = {
                              hasNegative: false,
                              positive: format,
                              negative: format,
                              zero: format,
                          };
                          // escape literals so semi-colon in a literal isn't interpreted as a delimiter
                          // NOTE: OK to use the literals extracted here for all three components before since the literals are indexed.
                          // For example, "'pos-lit';'neg-lit'" will get preserved as "\uE000;\uE001" and the literal array will be
                          // ['pos-lit', 'neg-lit']. When the negative components is restored, its \uE001 will select the second
                          // literal.
                          var literals = [];
                          format = formattingEncoder.preserveLiterals(format, literals);
                          var signSpecificFormats = format.split(numberFormat.NumberFormatComponentsDelimeter);
                          var formatCount = signSpecificFormats.length;
                          if (formatCount > 1) {
                              if (literals.length !== 0)
                                  signSpecificFormats = _.map(signSpecificFormats, function (signSpecificFormat) { return formattingEncoder.restoreLiterals(signSpecificFormat, literals); });
                              signFormat.hasNegative = true;
                              signFormat.positive = signFormat.zero = signSpecificFormats[0];
                              signFormat.negative = signSpecificFormats[1];
                              if (formatCount > 2)
                                  signFormat.zero = signSpecificFormats[2];
                          }
                          return signFormat;
                      }
                      numberFormat.getComponents = getComponents;
                      var _lastCustomFormatMeta;
                      /** Evaluates if the value can be formatted using the NumberFormat */
                      function canFormat(value) {
                          var result = typeof (value) === "number";
                          return result;
                      }
                      numberFormat.canFormat = canFormat;
                      function isStandardFormat(format) {
                          return StandardFormatRegex.test(format);
                      }
                      numberFormat.isStandardFormat = isStandardFormat;
                      /** Formats the number using specified format expression and culture */
                      function format(value, format, culture) {
                          format = format || "G";
                          try {
                              if (isStandardFormat(format))
                                  return formatNumberStandard(value, format, culture);
                              return formatNumberCustom(value, format, culture);
                          }
                          catch (e) {
                              return Globalize.format(value, undefined, culture);
                          }
                      }
                      numberFormat.format = format;
                      /** Performs a custom format with a value override.  Typically used for custom formats showing scaled values. */
                      function formatWithCustomOverride(value, format, nonScientificOverrideFormat, culture) {
                          return formatNumberCustom(value, format, culture, nonScientificOverrideFormat);
                      }
                      numberFormat.formatWithCustomOverride = formatWithCustomOverride;
                      /** Formats the number using standard format expression */
                      function formatNumberStandard(value, format, culture) {
                          var result;
                          var precision = (format.length > 1 ? parseInt(format.substr(1, format.length - 1), 10) : undefined);
                          var numberFormatInfo = culture.numberFormat;
                          var formatChar = format.charAt(0);
                          switch (formatChar) {
                              case "e":
                              case "E":
                                  if (precision === undefined) {
                                      precision = 6;
                                  }
                                  var mantissaDecimalDigits = stringExtensions.repeat("0", precision);
                                  format = "0." + mantissaDecimalDigits + formatChar + "+000";
                                  result = formatNumberCustom(value, format, culture);
                                  break;
                              case "f":
                              case "F":
                                  result = precision !== undefined ? value.toFixed(precision) : value.toFixed(numberFormatInfo.decimals);
                                  result = localize(result, numberFormatInfo);
                                  break;
                              case "g":
                              case "G":
                                  var abs = Math.abs(value);
                                  if (abs === 0 || (1E-4 <= abs && abs < 1E15)) {
                                      // For the range of 0.0001 to 1,000,000,000,000,000 - use the normal form
                                      result = precision !== undefined ? value.toPrecision(precision) : value.toString();
                                  }
                                  else {
                                      // Otherwise use exponential
                                      // Assert that value is a number and fall back on returning value if it is not
                                      if (typeof (value) !== "number")
                                          return String(value);
                                      result = precision !== undefined ? value.toExponential(precision) : value.toExponential();
                                      result = result.replace("e", "E");
                                  }
                                  result = localize(result, numberFormatInfo);
                                  break;
                              case "r":
                              case "R":
                                  result = value.toString();
                                  result = localize(result, numberFormatInfo);
                                  break;
                              case "x":
                              case "X":
                                  result = value.toString(16);
                                  if (formatChar === "X") {
                                      result = result.toUpperCase();
                                  }
                                  if (precision !== undefined) {
                                      var actualPrecision = result.length;
                                      var isNegative = value < 0;
                                      if (isNegative) {
                                          actualPrecision--;
                                      }
                                      var paddingZerosCount = precision - actualPrecision;
                                      var paddingZeros = undefined;
                                      if (paddingZerosCount > 0) {
                                          paddingZeros = stringExtensions.repeat("0", paddingZerosCount);
                                      }
                                      if (isNegative) {
                                          result = "-" + paddingZeros + result.substr(1);
                                      }
                                      else {
                                          result = paddingZeros + result;
                                      }
                                  }
                                  result = localize(result, numberFormatInfo);
                                  break;
                              default:
                                  result = Globalize.format(value, format, culture);
                          }
                          return result;
                      }
                      /** Formats the number using custom format expression */
                      function formatNumberCustom(value, format, culture, nonScientificOverrideFormat) {
                          var result;
                          var numberFormatInfo = culture.numberFormat;
                          if (isFinite(value)) {
                              // Split format by positive[;negative;zero] pattern
                              var formatComponents = getComponents(format);
                              // Pick a format based on the sign of value
                              if (value > 0) {
                                  format = formatComponents.positive;
                              }
                              else if (value === 0) {
                                  format = formatComponents.zero;
                              }
                              else {
                                  format = formatComponents.negative;
                              }
                              // Normalize value if we have an explicit negative format
                              if (formatComponents.hasNegative)
                                  value = Math.abs(value);
                              // Get format metadata
                              var formatMeta = getCustomFormatMetadata(format, true /*calculatePrecision*/);
                              // Preserve literals and escaped chars
                              var literals = [];
                              if (formatMeta.hasLiterals) {
                                  format = formattingEncoder.preserveLiterals(format, literals);
                              }
                              // Scientific format
                              if (formatMeta.hasE && !nonScientificOverrideFormat) {
                                  var scientificMatch = RegExpExtensions.run(ScientificFormatRegex, format);
                                  if (scientificMatch) {
                                      // Case 2.1. Scientific custom format
                                      var formatM = format.substr(0, scientificMatch.index);
                                      var formatE = format.substr(scientificMatch.index + 2); // E(+|-)
                                      var precision = getCustomFormatPrecision(formatM, formatMeta);
                                      var scale = getCustomFormatScale(formatM, formatMeta);
                                      if (scale !== 1) {
                                          value = value * scale;
                                      }
                                      // Assert that value is a number and fall back on returning value if it is not
                                      if (typeof (value) !== "number")
                                          return String(value);
                                      var s = value.toExponential(precision);
                                      var indexOfE = s.indexOf("e");
                                      var mantissa = s.substr(0, indexOfE);
                                      var exp = s.substr(indexOfE + 1);
                                      var resultM = fuseNumberWithCustomFormat(mantissa, formatM, numberFormatInfo);
                                      var resultE = fuseNumberWithCustomFormat(exp, formatE, numberFormatInfo);
                                      if (resultE.charAt(0) === "+" && scientificMatch[0].charAt(1) !== "+") {
                                          resultE = resultE.substr(1);
                                      }
                                      var e = scientificMatch[0].charAt(0);
                                      result = resultM + e + resultE;
                                  }
                              }
                              // Non scientific format
                              if (result === undefined) {
                                  var valueFormatted = void 0;
                                  var isValueGlobalized = false;
                                  var precision = getCustomFormatPrecision(format, formatMeta);
                                  var scale = getCustomFormatScale(format, formatMeta);
                                  if (scale !== 1)
                                      value = value * scale;
                                  // Rounding
                                  value = parseFloat(toNonScientific(value, precision));
                                  if (!isFinite(value)) {
                                      // very large and small finite values can become infinite by parseFloat(toNonScientific())
                                      return Globalize.format(value, undefined);
                                  }
                                  if (nonScientificOverrideFormat) {
                                      // Get numeric format from format string
                                      var numericFormat = numberFormat.getNumericFormat(value, format);
                                      // Add separators and decimalFormat to nonScientificFormat
                                      nonScientificOverrideFormat = getNonScientificFormatWithPrecision(nonScientificOverrideFormat, numericFormat);
                                      // Format the value
                                      valueFormatted = formatting.formattingService.format(nonScientificOverrideFormat, [value], culture.name);
                                      isValueGlobalized = true;
                                  }
                                  else
                                      valueFormatted = toNonScientific(value, precision);
                                  result = fuseNumberWithCustomFormat(valueFormatted, format, numberFormatInfo, nonScientificOverrideFormat, isValueGlobalized);
                              }
                              if (formatMeta.hasLiterals) {
                                  result = formattingEncoder.restoreLiterals(result, literals, false);
                              }
                              _lastCustomFormatMeta = formatMeta;
                          }
                          else {
                              return Globalize.format(value, undefined);
                          }
                          return result;
                      }
                      /** Returns string with the fixed point respresentation of the number */
                      function toNonScientific(value, precision) {
                          var result = "";
                          var precisionZeros = 0;
                          // Double precision numbers support actual 15-16 decimal digits of precision.
                          if (precision > 16) {
                              precisionZeros = precision - 16;
                              precision = 16;
                          }
                          var digitsBeforeDecimalPoint = Double.log10(Math.abs(value));
                          if (digitsBeforeDecimalPoint < 16) {
                              if (digitsBeforeDecimalPoint > 0) {
                                  var maxPrecision = 16 - digitsBeforeDecimalPoint;
                                  if (precision > maxPrecision) {
                                      precisionZeros += precision - maxPrecision;
                                      precision = maxPrecision;
                                  }
                              }
                              result = value.toFixed(precision);
                          }
                          else if (digitsBeforeDecimalPoint === 16) {
                              result = value.toFixed(0);
                              precisionZeros += precision;
                              if (precisionZeros > 0) {
                                  result += ".";
                              }
                          }
                          else {
                              // Different browsers have different implementations of the toFixed().
                              // In IE it returns fixed format no matter what's the number. In FF and Chrome the method returns exponential format for numbers greater than 1E21.
                              // So we need to check for range and convert the to exponential with the max precision.
                              // Then we convert exponential string to fixed by removing the dot and padding with "power" zeros.
                              // Assert that value is a number and fall back on returning value if it is not
                              if (typeof (value) !== "number")
                                  return String(value);
                              result = value.toExponential(15);
                              var indexOfE = result.indexOf("e");
                              if (indexOfE > 0) {
                                  var indexOfDot = result.indexOf(".");
                                  var mantissa = result.substr(0, indexOfE);
                                  var exp = result.substr(indexOfE + 1);
                                  var powerZeros = parseInt(exp, 10) - (mantissa.length - indexOfDot - 1);
                                  result = mantissa.replace(".", "") + stringExtensions.repeat("0", powerZeros);
                                  if (precision > 0) {
                                      result = result + "." + stringExtensions.repeat("0", precision);
                                  }
                              }
                          }
                          if (precisionZeros > 0) {
                              result = result + stringExtensions.repeat("0", precisionZeros);
                          }
                          return result;
                      }
                      /**
                       * Returns the formatMetadata of the format
                       * When calculating precision and scale, if format string of
                       * positive[;negative;zero] => positive format will be used
                       * @param (required) format - format string
                       * @param (optional) calculatePrecision - calculate precision of positive format
                       * @param (optional) calculateScale - calculate scale of positive format
                       */
                      function getCustomFormatMetadata(format, calculatePrecision, calculateScale, calculatePartsPerScale) {
                          if (_lastCustomFormatMeta !== undefined && format === _lastCustomFormatMeta.format) {
                              return _lastCustomFormatMeta;
                          }
                          var literals = [];
                          var escaped = formattingEncoder.preserveLiterals(format, literals);
                          var result = {
                              format: format,
                              hasLiterals: literals.length !== 0,
                              hasE: false,
                              hasCommas: false,
                              hasDots: false,
                              hasPercent: false,
                              hasPermile: false,
                              precision: undefined,
                              scale: undefined,
                              partsPerScale: undefined,
                          };
                          for (var i = 0, length_1 = escaped.length; i < length_1; i++) {
                              var c = escaped.charAt(i);
                              switch (c) {
                                  case "e":
                                  case "E":
                                      result.hasE = true;
                                      break;
                                  case ",":
                                      result.hasCommas = true;
                                      break;
                                  case ".":
                                      result.hasDots = true;
                                      break;
                                  case "%":
                                      result.hasPercent = true;
                                      break;
                                  case "\u2030":
                                      result.hasPermile = true;
                                      break;
                              }
                          }
                          // Use positive format for calculating these values
                          var formatComponents = getComponents(format);
                          if (calculatePrecision)
                              result.precision = getCustomFormatPrecision(formatComponents.positive, result);
                          if (calculatePartsPerScale)
                              result.partsPerScale = getCustomFormatPartsPerScale(formatComponents.positive, result);
                          if (calculateScale)
                              result.scale = getCustomFormatScale(formatComponents.positive, result);
                          return result;
                      }
                      numberFormat.getCustomFormatMetadata = getCustomFormatMetadata;
                      /** Returns the decimal precision of format based on the number of # and 0 chars after the decimal point
                        * Important: The input format string needs to be split to the appropriate pos/neg/zero portion to work correctly */
                      function getCustomFormatPrecision(format, formatMeta) {
                          if (formatMeta.precision > -1) {
                              return formatMeta.precision;
                          }
                          var result = 0;
                          if (formatMeta.hasDots) {
                              if (formatMeta.hasLiterals) {
                                  format = formattingEncoder.removeLiterals(format);
                              }
                              var dotIndex = format.indexOf(".");
                              if (dotIndex > -1) {
                                  var count = format.length;
                                  for (var i = dotIndex; i < count; i++) {
                                      var char = format.charAt(i);
                                      if (char.match(NumericPlaceholderRegex))
                                          result++;
                                      // 0.00E+0 :: Break before counting 0 in
                                      // exponential portion of format string
                                      if (char === ExponentialFormatChar)
                                          break;
                                  }
                                  result = Math.min(19, result);
                              }
                          }
                          formatMeta.precision = result;
                          return result;
                      }
                      function getCustomFormatPartsPerScale(format, formatMeta) {
                          if (formatMeta.partsPerScale != null)
                              return formatMeta.partsPerScale;
                          var result = 1;
                          if (formatMeta.hasPercent && format.indexOf("%") > -1) {
                              result = result * 100;
                          }
                          if (formatMeta.hasPermile && format.indexOf(/* ‰ */ "\u2030") > -1) {
                              result = result * 1000;
                          }
                          formatMeta.partsPerScale = result;
                          return result;
                      }
                      /** Returns the scale factor of the format based on the "%" and scaling "," chars in the format */
                      function getCustomFormatScale(format, formatMeta) {
                          if (formatMeta.scale > -1) {
                              return formatMeta.scale;
                          }
                          var result = getCustomFormatPartsPerScale(format, formatMeta);
                          if (formatMeta.hasCommas) {
                              var dotIndex = format.indexOf(".");
                              if (dotIndex === -1) {
                                  dotIndex = format.length;
                              }
                              for (var i = dotIndex - 1; i > -1; i--) {
                                  var char = format.charAt(i);
                                  if (char === ",") {
                                      result = result / 1000;
                                  }
                                  else {
                                      break;
                                  }
                              }
                          }
                          formatMeta.scale = result;
                          return result;
                      }
                      function fuseNumberWithCustomFormat(value, format, numberFormatInfo, nonScientificOverrideFormat, isValueGlobalized) {
                          var suppressModifyValue = !!nonScientificOverrideFormat;
                          var formatParts = format.split(".", 2);
                          if (formatParts.length === 2) {
                              var wholeFormat = formatParts[0];
                              var fractionFormat = formatParts[1];
                              var displayUnit = "";
                              // Remove display unit from value before splitting on "." as localized display units sometimes end with "."
                              if (nonScientificOverrideFormat) {
                                  displayUnit = nonScientificOverrideFormat.replace(NumericalPlaceHolderRegex, "");
                                  value = value.replace(displayUnit, "");
                              }
                              var globalizedDecimalSeparator = numberFormatInfo["."];
                              var decimalSeparator = isValueGlobalized ? globalizedDecimalSeparator : ".";
                              var valueParts = value.split(decimalSeparator, 2);
                              var wholeValue = valueParts.length === 1 ? valueParts[0] + displayUnit : valueParts[0];
                              var fractionValue = valueParts.length === 2 ? valueParts[1] + displayUnit : "";
                              fractionValue = fractionValue.replace(TrailingZerosRegex, "");
                              var wholeFormattedValue = fuseNumberWithCustomFormatLeft(wholeValue, wholeFormat, numberFormatInfo, suppressModifyValue);
                              var fractionFormattedValue = fuseNumberWithCustomFormatRight(fractionValue, fractionFormat, suppressModifyValue);
                              if (fractionFormattedValue.fmtOnly || fractionFormattedValue.value === "")
                                  return wholeFormattedValue + fractionFormattedValue.value;
                              return wholeFormattedValue + globalizedDecimalSeparator + fractionFormattedValue.value;
                          }
                          return fuseNumberWithCustomFormatLeft(value, format, numberFormatInfo, suppressModifyValue);
                      }
                      function fuseNumberWithCustomFormatLeft(value, format, numberFormatInfo, suppressModifyValue) {
                          var groupSymbolIndex = format.indexOf(",");
                          var enableGroups = groupSymbolIndex > -1 && groupSymbolIndex < Math.max(format.lastIndexOf("0"), format.lastIndexOf("#")) && numberFormatInfo[","];
                          var groupDigitCount = 0;
                          var groupIndex = 0;
                          var groupSizes = numberFormatInfo.groupSizes || [3];
                          var groupSize = groupSizes[0];
                          var groupSeparator = numberFormatInfo[","];
                          var sign = "";
                          var firstChar = value.charAt(0);
                          if (firstChar === "+" || firstChar === "-") {
                              sign = numberFormatInfo[firstChar];
                              value = value.substr(1);
                          }
                          var isZero = value === "0";
                          var result = "";
                          var leftBuffer = "";
                          var vi = value.length - 1;
                          var fmtOnly = true;
                          // Iterate through format chars and replace 0 and # with the digits from the value string
                          for (var fi = format.length - 1; fi > -1; fi--) {
                              var formatChar = format.charAt(fi);
                              switch (formatChar) {
                                  case ZeroPlaceholder:
                                  case DigitPlaceholder:
                                      fmtOnly = false;
                                      if (leftBuffer !== "") {
                                          result = leftBuffer + result;
                                          leftBuffer = "";
                                      }
                                      if (!suppressModifyValue) {
                                          if (vi > -1 || formatChar === ZeroPlaceholder) {
                                              if (enableGroups) {
                                                  // If the groups are enabled we'll need to keep track of the current group index and periodically insert group separator,
                                                  if (groupDigitCount === groupSize) {
                                                      result = groupSeparator + result;
                                                      groupIndex++;
                                                      if (groupIndex < groupSizes.length) {
                                                          groupSize = groupSizes[groupIndex];
                                                      }
                                                      groupDigitCount = 1;
                                                  }
                                                  else {
                                                      groupDigitCount++;
                                                  }
                                              }
                                          }
                                          if (vi > -1) {
                                              if (isZero && formatChar === DigitPlaceholder) {
                                              }
                                              else {
                                                  result = value.charAt(vi) + result;
                                              }
                                              vi--;
                                          }
                                          else if (formatChar !== DigitPlaceholder) {
                                              result = formatChar + result;
                                          }
                                      }
                                      break;
                                  case ",":
                                      // We should skip all the , chars
                                      break;
                                  default:
                                      leftBuffer = formatChar + leftBuffer;
                                      break;
                              }
                          }
                          // If the value didn't fit into the number of zeros provided in the format then we should insert the missing part of the value into the result
                          if (!suppressModifyValue) {
                              if (vi > -1 && result !== "") {
                                  if (enableGroups) {
                                      while (vi > -1) {
                                          if (groupDigitCount === groupSize) {
                                              result = groupSeparator + result;
                                              groupIndex++;
                                              if (groupIndex < groupSizes.length) {
                                                  groupSize = groupSizes[groupIndex];
                                              }
                                              groupDigitCount = 1;
                                          }
                                          else {
                                              groupDigitCount++;
                                          }
                                          result = value.charAt(vi) + result;
                                          vi--;
                                      }
                                  }
                                  else {
                                      result = value.substr(0, vi + 1) + result;
                                  }
                              }
                              // Insert sign in front of the leftBuffer and result
                              return sign + leftBuffer + result;
                          }
                          if (fmtOnly)
                              // If the format doesn't specify any digits to be displayed, then just return the format we've parsed up until now.
                              return sign + leftBuffer + result;
                          return sign + leftBuffer + value + result;
                      }
                      function fuseNumberWithCustomFormatRight(value, format, suppressModifyValue) {
                          var vi = 0;
                          var fCount = format.length;
                          var vCount = value.length;
                          if (suppressModifyValue) {
                              var lastChar = format.charAt(fCount - 1);
                              if (!lastChar.match(NumericPlaceholderRegex))
                                  return {
                                      value: value + lastChar,
                                      fmtOnly: value === "",
                                  };
                              return {
                                  value: value,
                                  fmtOnly: value === "",
                              };
                          }
                          var result = "", fmtOnly = true;
                          for (var fi = 0; fi < fCount; fi++) {
                              var formatChar = format.charAt(fi);
                              if (vi < vCount) {
                                  switch (formatChar) {
                                      case ZeroPlaceholder:
                                      case DigitPlaceholder:
                                          result += value[vi++];
                                          fmtOnly = false;
                                          break;
                                      default:
                                          result += formatChar;
                                  }
                              }
                              else {
                                  if (formatChar !== DigitPlaceholder) {
                                      result += formatChar;
                                      fmtOnly = fmtOnly && (formatChar !== ZeroPlaceholder);
                                  }
                              }
                          }
                          return {
                              value: result,
                              fmtOnly: fmtOnly,
                          };
                      }
                      function localize(value, dictionary) {
                          var plus = dictionary["+"];
                          var minus = dictionary["-"];
                          var dot = dictionary["."];
                          var comma = dictionary[","];
                          if (plus === "+" && minus === "-" && dot === "." && comma === ",") {
                              return value;
                          }
                          var count = value.length;
                          var result = "";
                          for (var i = 0; i < count; i++) {
                              var char = value.charAt(i);
                              switch (char) {
                                  case "+":
                                      result = result + plus;
                                      break;
                                  case "-":
                                      result = result + minus;
                                      break;
                                  case ".":
                                      result = result + dot;
                                      break;
                                  case ",":
                                      result = result + comma;
                                      break;
                                  default:
                                      result = result + char;
                                      break;
                              }
                          }
                          return result;
                      }
                  })(numberFormat = formatting.numberFormat || (formatting.numberFormat = {}));
                  /** DateTimeScaleFormatInfo is used to calculate and keep the Date formats used for different units supported by the DateTimeScaleModel */
                  var DateTimeScaleFormatInfo = (function () {
                      // Constructor
                      /**
                       * Creates new instance of the DateTimeScaleFormatInfo class.
                       * @param culture - culture which calendar info is going to be used to derive the formats.
                       */
                      function DateTimeScaleFormatInfo(culture) {
                          var calendar = culture.calendar;
                          var patterns = calendar.patterns;
                          var monthAbbreviations = calendar["months"]["namesAbbr"];
                          var cultureHasMonthAbbr = monthAbbreviations && monthAbbreviations[0];
                          var yearMonthPattern = patterns["Y"];
                          var monthDayPattern = patterns["M"];
                          var fullPattern = patterns["f"];
                          var longTimePattern = patterns["T"];
                          var shortTimePattern = patterns["t"];
                          var separator = fullPattern.indexOf(",") > -1 ? ", " : " ";
                          var hasYearSymbol = yearMonthPattern.indexOf("yyyy'") === 0 && yearMonthPattern.length > 6 && yearMonthPattern[6] === "\'";
                          this.YearPattern = hasYearSymbol ? yearMonthPattern.substr(0, 7) : "yyyy";
                          var yearPos = fullPattern.indexOf("yy");
                          var monthPos = fullPattern.indexOf("MMMM");
                          this.MonthPattern = cultureHasMonthAbbr && monthPos > -1 ? (yearPos > monthPos ? "MMM yyyy" : "yyyy MMM") : yearMonthPattern;
                          this.DayPattern = cultureHasMonthAbbr ? monthDayPattern.replace("MMMM", "MMM") : monthDayPattern;
                          var minutePos = fullPattern.indexOf("mm");
                          var pmPos = fullPattern.indexOf("tt");
                          var shortHourPattern = pmPos > -1 ? shortTimePattern.replace(":mm ", "") : shortTimePattern;
                          this.HourPattern = yearPos < minutePos ? this.DayPattern + separator + shortHourPattern : shortHourPattern + separator + this.DayPattern;
                          this.MinutePattern = shortTimePattern;
                          this.SecondPattern = longTimePattern;
                          this.MillisecondPattern = longTimePattern.replace("ss", "ss.fff");
                          // Special cases
                          switch (culture.name) {
                              case "fi-FI":
                                  this.DayPattern = this.DayPattern.replace("'ta'", ""); // Fix for finish 'ta' suffix for month names.
                                  this.HourPattern = this.HourPattern.replace("'ta'", "");
                                  break;
                          }
                      }
                      // Methods
                      /**
                       * Returns the format string of the provided DateTimeUnit.
                       * @param unit - date or time unit
                       */
                      DateTimeScaleFormatInfo.prototype.getFormatString = function (unit) {
                          switch (unit) {
                              case DateTimeUnit.Year:
                                  return this.YearPattern;
                              case DateTimeUnit.Month:
                                  return this.MonthPattern;
                              case DateTimeUnit.Week:
                              case DateTimeUnit.Day:
                                  return this.DayPattern;
                              case DateTimeUnit.Hour:
                                  return this.HourPattern;
                              case DateTimeUnit.Minute:
                                  return this.MinutePattern;
                              case DateTimeUnit.Second:
                                  return this.SecondPattern;
                              case DateTimeUnit.Millisecond:
                                  return this.MillisecondPattern;
                          }
                      };
                      return DateTimeScaleFormatInfo;
                  }());
                  formatting.formattingService = new FormattingService();
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  /** The system used to determine display units used during formatting */
                  (function (DisplayUnitSystemType) {
                      /** Default display unit system, which saves space by using units such as K, M, bn with PowerView rules for when to pick a unit. Suitable for chart axes. */
                      DisplayUnitSystemType[DisplayUnitSystemType["Default"] = 0] = "Default";
                      /** A verbose display unit system that will only respect the formatting defined in the model. Suitable for explore mode single-value cards. */
                      DisplayUnitSystemType[DisplayUnitSystemType["Verbose"] = 1] = "Verbose";
                      /**
                       * A display unit system that uses units such as K, M, bn if we have at least one of those units (e.g. 0.9M is not valid as it's less than 1 million).
                       * Suitable for dashboard tile cards
                       */
                      DisplayUnitSystemType[DisplayUnitSystemType["WholeUnits"] = 2] = "WholeUnits";
                      /**A display unit system that also contains Auto and None units for data labels*/
                      DisplayUnitSystemType[DisplayUnitSystemType["DataLabels"] = 3] = "DataLabels";
                  })(formatting.DisplayUnitSystemType || (formatting.DisplayUnitSystemType = {}));
                  var DisplayUnitSystemType = formatting.DisplayUnitSystemType;
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var Double = powerbi.extensibility.utils.type.Double;
                  var NumberFormat = powerbi.extensibility.utils.formatting.numberFormat;
                  var formattingService = powerbi.extensibility.utils.formatting.formattingService;
                  // Constants
                  var maxExponent = 24;
                  var defaultScientificBigNumbersBoundary = 1E15;
                  var scientificSmallNumbersBoundary = 1E-4;
                  var PERCENTAGE_FORMAT = "%";
                  var SCIENTIFIC_FORMAT = "E+0";
                  var DEFAULT_SCIENTIFIC_FORMAT = "0.##" + SCIENTIFIC_FORMAT;
                  // Regular expressions
                  /**
                   * This regex looks for strings that match one of the following conditions:
                   *   - Optionally contain "0", "#", followed by a period, followed by at least one "0" or "#" (Ex. ###,000.###)
                   *   - Contains at least one of "0", "#", or "," (Ex. ###,000)
                   *   - Contain a "g" (indicates to use the general .NET numeric format string)
                   * The entire string (start to end) must match, and the match is not case-sensitive.
                   */
                  var SUPPORTED_SCIENTIFIC_FORMATS = /^([0\#,]*\.[0\#]+|[0\#,]+|g)$/i;
                  var DisplayUnit = (function () {
                      function DisplayUnit() {
                      }
                      // Methods
                      DisplayUnit.prototype.project = function (value) {
                          if (this.value) {
                              return Double.removeDecimalNoise(value / this.value);
                          }
                          else {
                              return value;
                          }
                      };
                      DisplayUnit.prototype.reverseProject = function (value) {
                          if (this.value) {
                              return value * this.value;
                          }
                          else {
                              return value;
                          }
                      };
                      DisplayUnit.prototype.isApplicableTo = function (value) {
                          value = Math.abs(value);
                          var precision = Double.getPrecision(value, 3);
                          return Double.greaterOrEqualWithPrecision(value, this.applicableRangeMin, precision) && Double.lessWithPrecision(value, this.applicableRangeMax, precision);
                      };
                      DisplayUnit.prototype.isScaling = function () {
                          return this.value > 1;
                      };
                      return DisplayUnit;
                  }());
                  formatting.DisplayUnit = DisplayUnit;
                  var DisplayUnitSystem = (function () {
                      // Constructor
                      function DisplayUnitSystem(units) {
                          this.units = units ? units : [];
                      }
                      Object.defineProperty(DisplayUnitSystem.prototype, "title", {
                          // Properties
                          get: function () {
                              return this.displayUnit ? this.displayUnit.title : undefined;
                          },
                          enumerable: true,
                          configurable: true
                      });
                      // Methods
                      DisplayUnitSystem.prototype.update = function (value) {
                          if (value === undefined)
                              return;
                          this.unitBaseValue = value;
                          this.displayUnit = this.findApplicableDisplayUnit(value);
                      };
                      DisplayUnitSystem.prototype.findApplicableDisplayUnit = function (value) {
                          for (var _i = 0, _a = this.units; _i < _a.length; _i++) {
                              var unit = _a[_i];
                              if (unit.isApplicableTo(value))
                                  return unit;
                          }
                          return undefined;
                      };
                      DisplayUnitSystem.prototype.format = function (value, format, decimals, trailingZeros, cultureSelector) {
                          decimals = this.getNumberOfDecimalsForFormatting(format, decimals);
                          var nonScientificFormat = "";
                          if (this.isFormatSupported(format)
                              && !this.hasScientitifcFormat(format)
                              && this.isScalingUnit()
                              && this.shouldRespectScalingUnit(format)) {
                              value = this.displayUnit.project(value);
                              nonScientificFormat = this.displayUnit.labelFormat;
                          }
                          return this.formatHelper({
                              value: value,
                              nonScientificFormat: nonScientificFormat,
                              format: format,
                              decimals: decimals,
                              trailingZeros: trailingZeros,
                              cultureSelector: cultureSelector
                          });
                      };
                      DisplayUnitSystem.prototype.isFormatSupported = function (format) {
                          return !DisplayUnitSystem.UNSUPPORTED_FORMATS.test(format);
                      };
                      DisplayUnitSystem.prototype.isPercentageFormat = function (format) {
                          return format && format.indexOf(PERCENTAGE_FORMAT) >= 0;
                      };
                      DisplayUnitSystem.prototype.shouldRespectScalingUnit = function (format) {
                          return true;
                      };
                      DisplayUnitSystem.prototype.getNumberOfDecimalsForFormatting = function (format, decimals) {
                          return decimals;
                      };
                      DisplayUnitSystem.prototype.isScalingUnit = function () {
                          return this.displayUnit && this.displayUnit.isScaling();
                      };
                      DisplayUnitSystem.prototype.formatHelper = function (options) {
                          var value = options.value, nonScientificFormat = options.nonScientificFormat, cultureSelector = options.cultureSelector, format = options.format, decimals = options.decimals, trailingZeros = options.trailingZeros;
                          // If the format is "general" and we want to override the number of decimal places then use the default numeric format string.
                          if ((format === "g" || format === "G") && decimals != null) {
                              format = "#,0.00";
                          }
                          format = NumberFormat.addDecimalsToFormat(format, decimals, trailingZeros);
                          if (format && !formattingService.isStandardNumberFormat(format))
                              return formattingService.formatNumberWithCustomOverride(value, format, nonScientificFormat);
                          if (!format) {
                              format = "G";
                          }
                          if (!nonScientificFormat) {
                              nonScientificFormat = "{0}";
                          }
                          var text = formattingService.formatValue(value, format, cultureSelector);
                          return formattingService.format(nonScientificFormat, [text]);
                      };
                      /** Formats a single value by choosing an appropriate base for the DisplayUnitSystem before formatting. */
                      DisplayUnitSystem.prototype.formatSingleValue = function (value, format, decimals, trailingZeros, cultureSelector) {
                          // Change unit base to a value appropriate for this value
                          this.update(this.shouldUseValuePrecision(value) ? Double.getPrecision(value, 8) : value);
                          return this.format(value, format, decimals, trailingZeros, cultureSelector);
                      };
                      DisplayUnitSystem.prototype.shouldUseValuePrecision = function (value) {
                          if (this.units.length === 0)
                              return true;
                          // Check if the value is big enough to have a valid unit by checking against the smallest unit (that it's value bigger than 1).
                          var applicableRangeMin = 0;
                          for (var i = 0; i < this.units.length; i++) {
                              if (this.units[i].isScaling()) {
                                  applicableRangeMin = this.units[i].applicableRangeMin;
                                  break;
                              }
                          }
                          return Math.abs(value) < applicableRangeMin;
                      };
                      DisplayUnitSystem.prototype.isScientific = function (value) {
                          return value < -defaultScientificBigNumbersBoundary || value > defaultScientificBigNumbersBoundary ||
                              (-scientificSmallNumbersBoundary < value && value < scientificSmallNumbersBoundary && value !== 0);
                      };
                      DisplayUnitSystem.prototype.hasScientitifcFormat = function (format) {
                          return format && format.toUpperCase().indexOf("E") !== -1;
                      };
                      DisplayUnitSystem.prototype.supportsScientificFormat = function (format) {
                          if (format)
                              return SUPPORTED_SCIENTIFIC_FORMATS.test(format);
                          return true;
                      };
                      DisplayUnitSystem.prototype.shouldFallbackToScientific = function (value, format) {
                          return !this.hasScientitifcFormat(format)
                              && this.supportsScientificFormat(format)
                              && this.isScientific(value);
                      };
                      DisplayUnitSystem.prototype.getScientificFormat = function (data, format, decimals, trailingZeros) {
                          // Use scientific format outside of the range
                          if (this.isFormatSupported(format) && this.shouldFallbackToScientific(data, format)) {
                              var numericFormat = NumberFormat.getNumericFormat(data, format);
                              if (decimals)
                                  numericFormat = NumberFormat.addDecimalsToFormat(numericFormat ? numericFormat : "0", Math.abs(decimals), trailingZeros);
                              if (numericFormat)
                                  return numericFormat + SCIENTIFIC_FORMAT;
                              else
                                  return DEFAULT_SCIENTIFIC_FORMAT;
                          }
                          return format;
                      };
                      DisplayUnitSystem.UNSUPPORTED_FORMATS = /^(p\d*)|(e\d*)$/i;
                      return DisplayUnitSystem;
                  }());
                  formatting.DisplayUnitSystem = DisplayUnitSystem;
                  /** Provides a unit system that is defined by formatting in the model, and is suitable for visualizations shown in single number visuals in explore mode. */
                  var NoDisplayUnitSystem = (function (_super) {
                      __extends(NoDisplayUnitSystem, _super);
                      // Constructor
                      function NoDisplayUnitSystem() {
                          _super.call(this, []);
                      }
                      return NoDisplayUnitSystem;
                  }(DisplayUnitSystem));
                  formatting.NoDisplayUnitSystem = NoDisplayUnitSystem;
                  /** Provides a unit system that creates a more concise format for displaying values. This is suitable for most of the cases where
                      we are showing values (chart axes) and as such it is the default unit system. */
                  var DefaultDisplayUnitSystem = (function (_super) {
                      __extends(DefaultDisplayUnitSystem, _super);
                      // Constructor
                      function DefaultDisplayUnitSystem(unitLookup) {
                          _super.call(this, DefaultDisplayUnitSystem.getUnits(unitLookup));
                      }
                      // Methods
                      DefaultDisplayUnitSystem.prototype.format = function (data, format, decimals, trailingZeros, cultureSelector) {
                          format = this.getScientificFormat(data, format, decimals, trailingZeros);
                          return _super.prototype.format.call(this, data, format, decimals, trailingZeros, cultureSelector);
                      };
                      DefaultDisplayUnitSystem.reset = function () {
                          DefaultDisplayUnitSystem.units = null;
                      };
                      DefaultDisplayUnitSystem.getUnits = function (unitLookup) {
                          if (!DefaultDisplayUnitSystem.units) {
                              DefaultDisplayUnitSystem.units = createDisplayUnits(unitLookup, function (value, previousUnitValue, min) {
                                  // When dealing with millions/billions/trillions we need to switch to millions earlier: for example instead of showing 100K 200K 300K we should show 0.1M 0.2M 0.3M etc
                                  if (value - previousUnitValue >= 1000) {
                                      return value / 10;
                                  }
                                  return min;
                              });
                              // Ensure last unit has max of infinity
                              DefaultDisplayUnitSystem.units[DefaultDisplayUnitSystem.units.length - 1].applicableRangeMax = Infinity;
                          }
                          return DefaultDisplayUnitSystem.units;
                      };
                      return DefaultDisplayUnitSystem;
                  }(DisplayUnitSystem));
                  formatting.DefaultDisplayUnitSystem = DefaultDisplayUnitSystem;
                  /** Provides a unit system that creates a more concise format for displaying values, but only allows showing a unit if we have at least
                      one of those units (e.g. 0.9M is not allowed since it's less than 1 million). This is suitable for cases such as dashboard tiles
                      where we have restricted space but do not want to show partial units. */
                  var WholeUnitsDisplayUnitSystem = (function (_super) {
                      __extends(WholeUnitsDisplayUnitSystem, _super);
                      // Constructor
                      function WholeUnitsDisplayUnitSystem(unitLookup) {
                          _super.call(this, WholeUnitsDisplayUnitSystem.getUnits(unitLookup));
                      }
                      WholeUnitsDisplayUnitSystem.reset = function () {
                          WholeUnitsDisplayUnitSystem.units = null;
                      };
                      WholeUnitsDisplayUnitSystem.getUnits = function (unitLookup) {
                          if (!WholeUnitsDisplayUnitSystem.units) {
                              WholeUnitsDisplayUnitSystem.units = createDisplayUnits(unitLookup);
                              // Ensure last unit has max of infinity
                              WholeUnitsDisplayUnitSystem.units[WholeUnitsDisplayUnitSystem.units.length - 1].applicableRangeMax = Infinity;
                          }
                          return WholeUnitsDisplayUnitSystem.units;
                      };
                      WholeUnitsDisplayUnitSystem.prototype.format = function (data, format, decimals, trailingZeros, cultureSelector) {
                          format = this.getScientificFormat(data, format, decimals, trailingZeros);
                          return _super.prototype.format.call(this, data, format, decimals, trailingZeros, cultureSelector);
                      };
                      return WholeUnitsDisplayUnitSystem;
                  }(DisplayUnitSystem));
                  formatting.WholeUnitsDisplayUnitSystem = WholeUnitsDisplayUnitSystem;
                  var DataLabelsDisplayUnitSystem = (function (_super) {
                      __extends(DataLabelsDisplayUnitSystem, _super);
                      function DataLabelsDisplayUnitSystem(unitLookup) {
                          _super.call(this, DataLabelsDisplayUnitSystem.getUnits(unitLookup));
                      }
                      DataLabelsDisplayUnitSystem.prototype.isFormatSupported = function (format) {
                          return !DataLabelsDisplayUnitSystem.UNSUPPORTED_FORMATS.test(format);
                      };
                      DataLabelsDisplayUnitSystem.getUnits = function (unitLookup) {
                          if (!DataLabelsDisplayUnitSystem.units) {
                              var units = [];
                              var adjustMinBasedOnPreviousUnit = function (value, previousUnitValue, min) {
                                  // Never returns true, we are always ignoring
                                  // We do not early switch (e.g. 100K instead of 0.1M)
                                  // Intended? If so, remove this function, otherwise, remove if statement
                                  if (value === -1)
                                      if (value - previousUnitValue >= 1000) {
                                          return value / 10;
                                      }
                                  return min;
                              };
                              // Add Auto & None
                              var names = unitLookup(-1);
                              addUnitIfNonEmpty(units, DataLabelsDisplayUnitSystem.AUTO_DISPLAYUNIT_VALUE, names.title, names.format, adjustMinBasedOnPreviousUnit);
                              names = unitLookup(0);
                              addUnitIfNonEmpty(units, DataLabelsDisplayUnitSystem.NONE_DISPLAYUNIT_VALUE, names.title, names.format, adjustMinBasedOnPreviousUnit);
                              // Add normal units
                              DataLabelsDisplayUnitSystem.units = units.concat(createDisplayUnits(unitLookup, adjustMinBasedOnPreviousUnit));
                              // Ensure last unit has max of infinity
                              DataLabelsDisplayUnitSystem.units[DataLabelsDisplayUnitSystem.units.length - 1].applicableRangeMax = Infinity;
                          }
                          return DataLabelsDisplayUnitSystem.units;
                      };
                      DataLabelsDisplayUnitSystem.prototype.format = function (data, format, decimals, trailingZeros, cultureSelector) {
                          format = this.getScientificFormat(data, format, decimals, trailingZeros);
                          return _super.prototype.format.call(this, data, format, decimals, trailingZeros, cultureSelector);
                      };
                      // Constants
                      DataLabelsDisplayUnitSystem.AUTO_DISPLAYUNIT_VALUE = 0;
                      DataLabelsDisplayUnitSystem.NONE_DISPLAYUNIT_VALUE = 1;
                      DataLabelsDisplayUnitSystem.UNSUPPORTED_FORMATS = /^(e\d*)$/i;
                      return DataLabelsDisplayUnitSystem;
                  }(DisplayUnitSystem));
                  formatting.DataLabelsDisplayUnitSystem = DataLabelsDisplayUnitSystem;
                  function createDisplayUnits(unitLookup, adjustMinBasedOnPreviousUnit) {
                      var units = [];
                      for (var i = 3; i < maxExponent; i++) {
                          var names = unitLookup(i);
                          if (names)
                              addUnitIfNonEmpty(units, Double.pow10(i), names.title, names.format, adjustMinBasedOnPreviousUnit);
                      }
                      return units;
                  }
                  function addUnitIfNonEmpty(units, value, title, labelFormat, adjustMinBasedOnPreviousUnit) {
                      if (title || labelFormat) {
                          var min = value;
                          if (units.length > 0) {
                              var previousUnit = units[units.length - 1];
                              if (adjustMinBasedOnPreviousUnit)
                                  min = adjustMinBasedOnPreviousUnit(value, previousUnit.value, min);
                              previousUnit.applicableRangeMax = min;
                          }
                          var unit = new DisplayUnit();
                          unit.value = value;
                          unit.applicableRangeMin = min;
                          unit.applicableRangeMax = min * 1000;
                          unit.title = title;
                          unit.labelFormat = labelFormat;
                          units.push(unit);
                      }
                  }
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  /**
   * Contains functions/constants to aid in text manupilation.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  var textUtil;
                  (function (textUtil) {
                      /**
                       * Remove breaking spaces from given string and replace by none breaking space (&nbsp).
                       */
                      function removeBreakingSpaces(str) {
                          return str.toString().replace(new RegExp(" ", "g"), "&nbsp");
                      }
                      textUtil.removeBreakingSpaces = removeBreakingSpaces;
                      /**
                       * Remove ellipses from a given string
                       */
                      function removeEllipses(str) {
                          return str.replace(/(…)|(\.\.\.)/g, "");
                      }
                      textUtil.removeEllipses = removeEllipses;
                      /**
                      * Replace every whitespace (0x20) with Non-Breaking Space (0xA0)
                       * @param {string} txt String to replace White spaces
                       * @returns Text after replcing white spaces
                       */
                      function replaceSpaceWithNBSP(txt) {
                          if (txt != null) {
                              return txt.replace(/ /g, "\xA0");
                          }
                      }
                      textUtil.replaceSpaceWithNBSP = replaceSpaceWithNBSP;
                  })(textUtil = formatting.textUtil || (formatting.textUtil = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  /*
   *  Power BI Visualizations
   *
   *  Copyright (c) Microsoft Corporation
   *  All rights reserved.
   *  MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the ""Software""), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  var powerbi;
  (function (powerbi) {
      var extensibility;
      (function (extensibility) {
          var utils;
          (function (utils) {
              var formatting;
              (function (formatting) {
                  // powerbi.extensibility.utils.type
                  var ValueType = powerbi.extensibility.utils.type.ValueType;
                  var PrimitiveType = powerbi.extensibility.utils.type.PrimitiveType;
                  var Double = powerbi.extensibility.utils.type.Double;
                  // powerbi.extensibility.utils.formatting
                  var stringExtensions = powerbi.extensibility.utils.formatting.stringExtensions;
                  var DisplayUnitSystemType = powerbi.extensibility.utils.formatting.DisplayUnitSystemType;
                  var DefaultDisplayUnitSystem = powerbi.extensibility.utils.formatting.DefaultDisplayUnitSystem;
                  var NumberFormat = powerbi.extensibility.utils.formatting.numberFormat;
                  var WholeUnitsDisplayUnitSystem = powerbi.extensibility.utils.formatting.WholeUnitsDisplayUnitSystem;
                  var DateTimeSequence = powerbi.extensibility.utils.formatting.DateTimeSequence;
                  var NoDisplayUnitSystem = powerbi.extensibility.utils.formatting.NoDisplayUnitSystem;
                  var DataLabelsDisplayUnitSystem = powerbi.extensibility.utils.formatting.DataLabelsDisplayUnitSystem;
                  var formattingService = powerbi.extensibility.utils.formatting.formattingService;
                  // powerbi.extensibility.utils.dataview
                  var DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;
                  var valueFormatter;
                  (function (valueFormatter) {
                      var StringExtensions = stringExtensions;
                      var BeautifiedFormat = {
                          "0.00 %;-0.00 %;0.00 %": "Percentage",
                          "0.0 %;-0.0 %;0.0 %": "Percentage1",
                      };
                      valueFormatter.DefaultIntegerFormat = "g";
                      valueFormatter.DefaultNumericFormat = "#,0.00";
                      valueFormatter.DefaultDateFormat = "d";
                      var defaultLocalizedStrings = {
                          "NullValue": "(Blank)",
                          "BooleanTrue": "True",
                          "BooleanFalse": "False",
                          "NaNValue": "NaN",
                          "InfinityValue": "+Infinity",
                          "NegativeInfinityValue": "-Infinity",
                          "RestatementComma": "{0}, {1}",
                          "RestatementCompoundAnd": "{0} and {1}",
                          "RestatementCompoundOr": "{0} or {1}",
                          "DisplayUnitSystem_EAuto_Title": "Auto",
                          "DisplayUnitSystem_E0_Title": "None",
                          "DisplayUnitSystem_E3_LabelFormat": "{0}K",
                          "DisplayUnitSystem_E3_Title": "Thousands",
                          "DisplayUnitSystem_E6_LabelFormat": "{0}M",
                          "DisplayUnitSystem_E6_Title": "Millions",
                          "DisplayUnitSystem_E9_LabelFormat": "{0}bn",
                          "DisplayUnitSystem_E9_Title": "Billions",
                          "DisplayUnitSystem_E12_LabelFormat": "{0}T",
                          "DisplayUnitSystem_E12_Title": "Trillions",
                          "Percentage": "#,0.##%",
                          "Percentage1": "#,0.#%",
                          "TableTotalLabel": "Total",
                          "Tooltip_HighlightedValueDisplayName": "Highlighted",
                          "Funnel_PercentOfFirst": "Percent of first",
                          "Funnel_PercentOfPrevious": "Percent of previous",
                          "Funnel_PercentOfFirst_Highlight": "Percent of first (highlighted)",
                          "Funnel_PercentOfPrevious_Highlight": "Percent of previous (highlighted)",
                          // Geotagging strings
                          "GeotaggingString_Continent": "continent",
                          "GeotaggingString_Continents": "continents",
                          "GeotaggingString_Country": "country",
                          "GeotaggingString_Countries": "countries",
                          "GeotaggingString_State": "state",
                          "GeotaggingString_States": "states",
                          "GeotaggingString_City": "city",
                          "GeotaggingString_Cities": "cities",
                          "GeotaggingString_Town": "town",
                          "GeotaggingString_Towns": "towns",
                          "GeotaggingString_Province": "province",
                          "GeotaggingString_Provinces": "provinces",
                          "GeotaggingString_County": "county",
                          "GeotaggingString_Counties": "counties",
                          "GeotaggingString_Village": "village",
                          "GeotaggingString_Villages": "villages",
                          "GeotaggingString_Post": "post",
                          "GeotaggingString_Zip": "zip",
                          "GeotaggingString_Code": "code",
                          "GeotaggingString_Place": "place",
                          "GeotaggingString_Places": "places",
                          "GeotaggingString_Address": "address",
                          "GeotaggingString_Addresses": "addresses",
                          "GeotaggingString_Street": "street",
                          "GeotaggingString_Streets": "streets",
                          "GeotaggingString_Longitude": "longitude",
                          "GeotaggingString_Longitude_Short": "lon",
                          "GeotaggingString_Longitude_Short2": "long",
                          "GeotaggingString_Latitude": "latitude",
                          "GeotaggingString_Latitude_Short": "lat",
                          "GeotaggingString_PostalCode": "postal code",
                          "GeotaggingString_PostalCodes": "postal codes",
                          "GeotaggingString_ZipCode": "zip code",
                          "GeotaggingString_ZipCodes": "zip codes",
                          "GeotaggingString_Territory": "territory",
                          "GeotaggingString_Territories": "territories",
                      };
                      function beautify(format) {
                          var key = BeautifiedFormat[format];
                          if (key)
                              return defaultLocalizedStrings[key] || format;
                          return format;
                      }
                      function describeUnit(exponent) {
                          var exponentLookup = (exponent === -1) ? "Auto" : exponent.toString();
                          var title = defaultLocalizedStrings["DisplayUnitSystem_E" + exponentLookup + "_Title"];
                          var format = (exponent <= 0) ? "{0}" : defaultLocalizedStrings["DisplayUnitSystem_E" + exponentLookup + "_LabelFormat"];
                          if (title || format)
                              return { title: title, format: format };
                      }
                      function getLocalizedString(stringId) {
                          return defaultLocalizedStrings[stringId];
                      }
                      valueFormatter.getLocalizedString = getLocalizedString;
                      // NOTE: Define default locale options, but these can be overriden by setLocaleOptions.
                      var localizationOptions = {
                          null: defaultLocalizedStrings["NullValue"],
                          true: defaultLocalizedStrings["BooleanTrue"],
                          false: defaultLocalizedStrings["BooleanFalse"],
                          NaN: defaultLocalizedStrings["NaNValue"],
                          infinity: defaultLocalizedStrings["InfinityValue"],
                          negativeInfinity: defaultLocalizedStrings["NegativeInfinityValue"],
                          beautify: function (format) { return beautify(format); },
                          describe: function (exponent) { return describeUnit(exponent); },
                          restatementComma: defaultLocalizedStrings["RestatementComma"],
                          restatementCompoundAnd: defaultLocalizedStrings["RestatementCompoundAnd"],
                          restatementCompoundOr: defaultLocalizedStrings["RestatementCompoundOr"],
                      };
                      var MaxScaledDecimalPlaces = 2;
                      var MaxValueForDisplayUnitRounding = 1000;
                      var MinIntegerValueForDisplayUnits = 10000;
                      var MinPrecisionForDisplayUnits = 2;
                      var DateTimeMetadataColumn = {
                          displayName: "",
                          type: ValueType.fromPrimitiveTypeAndCategory(PrimitiveType.DateTime),
                      };
                      function getFormatMetadata(format) {
                          return NumberFormat.getCustomFormatMetadata(format);
                      }
                      valueFormatter.getFormatMetadata = getFormatMetadata;
                      function setLocaleOptions(options) {
                          localizationOptions = options;
                          DefaultDisplayUnitSystem.reset();
                          WholeUnitsDisplayUnitSystem.reset();
                      }
                      valueFormatter.setLocaleOptions = setLocaleOptions;
                      function createDefaultFormatter(formatString, allowFormatBeautification, cultureSelector) {
                          var formatBeautified = allowFormatBeautification
                              ? localizationOptions.beautify(formatString)
                              : formatString;
                          return {
                              format: function (value) {
                                  if (value == null) {
                                      return localizationOptions.null;
                                  }
                                  return formatCore({
                                      value: value,
                                      cultureSelector: cultureSelector,
                                      format: formatBeautified
                                  });
                              }
                          };
                      }
                      valueFormatter.createDefaultFormatter = createDefaultFormatter;
                      /** Creates an IValueFormatter to be used for a range of values. */
                      function create(options) {
                          var format = !!options.allowFormatBeautification
                              ? localizationOptions.beautify(options.format)
                              : options.format;
                          var cultureSelector = options.cultureSelector;
                          if (shouldUseNumericDisplayUnits(options)) {
                              var displayUnitSystem_1 = createDisplayUnitSystem(options.displayUnitSystemType);
                              var singleValueFormattingMode_1 = !!options.formatSingleValues;
                              displayUnitSystem_1.update(Math.max(Math.abs(options.value || 0), Math.abs(options.value2 || 0)));
                              var forcePrecision_1 = options.precision != null;
                              var decimals_1;
                              if (forcePrecision_1)
                                  decimals_1 = -options.precision;
                              else if (displayUnitSystem_1.displayUnit && displayUnitSystem_1.displayUnit.value > 1)
                                  decimals_1 = -MaxScaledDecimalPlaces;
                              return {
                                  format: function (value) {
                                      var formattedValue = getStringFormat(value, true /*nullsAreBlank*/);
                                      if (!StringExtensions.isNullOrUndefinedOrWhiteSpaceString(formattedValue)) {
                                          return formattedValue;
                                      }
                                      // Round to Double.DEFAULT_PRECISION
                                      if (value
                                          && !displayUnitSystem_1.isScalingUnit()
                                          && Math.abs(value) < MaxValueForDisplayUnitRounding
                                          && !forcePrecision_1) {
                                          value = Double.roundToPrecision(value);
                                      }
                                      return singleValueFormattingMode_1
                                          ? displayUnitSystem_1.formatSingleValue(value, format, decimals_1, forcePrecision_1, cultureSelector)
                                          : displayUnitSystem_1.format(value, format, decimals_1, forcePrecision_1, cultureSelector);
                                  },
                                  displayUnit: displayUnitSystem_1.displayUnit,
                                  options: options
                              };
                          }
                          if (shouldUseDateUnits(options.value, options.value2, options.tickCount)) {
                              var unit_1 = DateTimeSequence.getIntervalUnit(options.value /* minDate */, options.value2 /* maxDate */, options.tickCount);
                              return {
                                  format: function (value) {
                                      if (value == null) {
                                          return localizationOptions.null;
                                      }
                                      var formatString = formattingService.dateFormatString(unit_1);
                                      return formatCore({
                                          value: value,
                                          cultureSelector: cultureSelector,
                                          format: formatString,
                                      });
                                  },
                                  options: options
                              };
                          }
                          return createDefaultFormatter(format, false, cultureSelector);
                      }
                      valueFormatter.create = create;
                      function format(value, format, allowFormatBeautification, cultureSelector) {
                          if (value == null) {
                              return localizationOptions.null;
                          }
                          var formatString = !!allowFormatBeautification
                              ? localizationOptions.beautify(format)
                              : format;
                          return formatCore({
                              value: value,
                              cultureSelector: cultureSelector,
                              format: formatString
                          });
                      }
                      valueFormatter.format = format;
                      /**
                       * Value formatting function to handle variant measures.
                       * For a Date/Time value within a non-date/time field, it's formatted with the default date/time formatString instead of as a number
                       * @param {any} value Value to be formatted
                       * @param {DataViewMetadataColumn} column Field which the value belongs to
                       * @param {DataViewObjectPropertyIdentifier} formatStringProp formatString Property ID
                       * @param {boolean} nullsAreBlank? Whether to show "(Blank)" instead of empty string for null values
                       * @returns Formatted value
                       */
                      function formatVariantMeasureValue(value, column, formatStringProp, nullsAreBlank, cultureSelector) {
                          // If column type is not datetime, but the value is of time datetime,
                          // then use the default date format string
                          if (!(column && column.type && column.type.dateTime) && value instanceof Date) {
                              var valueFormat = getFormatString(DateTimeMetadataColumn, null, false);
                              return formatCore({
                                  value: value,
                                  nullsAreBlank: nullsAreBlank,
                                  cultureSelector: cultureSelector,
                                  format: valueFormat
                              });
                          }
                          else {
                              var valueFormat = getFormatString(column, formatStringProp);
                              return formatCore({
                                  value: value,
                                  nullsAreBlank: nullsAreBlank,
                                  cultureSelector: cultureSelector,
                                  format: valueFormat
                              });
                          }
                      }
                      valueFormatter.formatVariantMeasureValue = formatVariantMeasureValue;
                      function createDisplayUnitSystem(displayUnitSystemType) {
                          if (displayUnitSystemType == null)
                              return new DefaultDisplayUnitSystem(localizationOptions.describe);
                          switch (displayUnitSystemType) {
                              case DisplayUnitSystemType.Default:
                                  return new DefaultDisplayUnitSystem(localizationOptions.describe);
                              case DisplayUnitSystemType.WholeUnits:
                                  return new WholeUnitsDisplayUnitSystem(localizationOptions.describe);
                              case DisplayUnitSystemType.Verbose:
                                  return new NoDisplayUnitSystem();
                              case DisplayUnitSystemType.DataLabels:
                                  return new DataLabelsDisplayUnitSystem(localizationOptions.describe);
                              default:
                                  return new DefaultDisplayUnitSystem(localizationOptions.describe);
                          }
                      }
                      valueFormatter.createDisplayUnitSystem = createDisplayUnitSystem;
                      function shouldUseNumericDisplayUnits(options) {
                          var value = options.value;
                          var value2 = options.value2;
                          var format = options.format;
                          // For singleValue visuals like card, gauge we don't want to roundoff data to the nearest thousands so format the whole number / integers below 10K to not use display units
                          if (options.formatSingleValues && format) {
                              if (Math.abs(value) < MinIntegerValueForDisplayUnits) {
                                  var isCustomFormat = !NumberFormat.isStandardFormat(format);
                                  if (isCustomFormat) {
                                      var precision = NumberFormat.getCustomFormatMetadata(format, true /*calculatePrecision*/).precision;
                                      if (precision < MinPrecisionForDisplayUnits)
                                          return false;
                                  }
                                  else if (Double.isInteger(value))
                                      return false;
                              }
                          }
                          if ((typeof value === "number") || (typeof value2 === "number")) {
                              return true;
                          }
                      }
                      function shouldUseDateUnits(value, value2, tickCount) {
                          // must check both value and value2 because we'll need to get an interval for date units
                          return (value instanceof Date) && (value2 instanceof Date) && (tickCount !== undefined && tickCount !== null);
                      }
                      /*
                       * Get the column format. Order of precendence is:
                       *  1. Column format
                       *  2. Default PowerView policy for column type
                       */
                      function getFormatString(column, formatStringProperty, suppressTypeFallback) {
                          if (column) {
                              if (formatStringProperty) {
                                  var propertyValue = DataViewObjects.getValue(column.objects, formatStringProperty);
                                  if (propertyValue)
                                      return propertyValue;
                              }
                              if (!suppressTypeFallback) {
                                  var columnType = column.type;
                                  if (columnType) {
                                      if (columnType.dateTime)
                                          return valueFormatter.DefaultDateFormat;
                                      if (columnType.integer) {
                                          if (columnType.temporal && columnType.temporal.year)
                                              return "0";
                                          return valueFormatter.DefaultIntegerFormat;
                                      }
                                      if (columnType.numeric)
                                          return valueFormatter.DefaultNumericFormat;
                                  }
                              }
                          }
                      }
                      valueFormatter.getFormatString = getFormatString;
                      function getFormatStringByColumn(column, suppressTypeFallback) {
                          if (column) {
                              if (column.format) {
                                  return column.format;
                              }
                              if (!suppressTypeFallback) {
                                  var columnType = column.type;
                                  if (columnType) {
                                      if (columnType.dateTime) {
                                          return valueFormatter.DefaultDateFormat;
                                      }
                                      if (columnType.integer) {
                                          if (columnType.temporal && columnType.temporal.year) {
                                              return "0";
                                          }
                                          return valueFormatter.DefaultIntegerFormat;
                                      }
                                      if (columnType.numeric) {
                                          return valueFormatter.DefaultNumericFormat;
                                      }
                                  }
                              }
                          }
                          return undefined;
                      }
                      valueFormatter.getFormatStringByColumn = getFormatStringByColumn;
                      function formatListCompound(strings, conjunction) {
                          var result;
                          if (!strings) {
                              return null;
                          }
                          var length = strings.length;
                          if (length > 0) {
                              result = strings[0];
                              var lastIndex = length - 1;
                              for (var i = 1, len = lastIndex; i < len; i++) {
                                  var value = strings[i];
                                  result = StringExtensions.format(localizationOptions.restatementComma, result, value);
                              }
                              if (length > 1) {
                                  var value = strings[lastIndex];
                                  result = StringExtensions.format(conjunction, result, value);
                              }
                          }
                          else {
                              result = null;
                          }
                          return result;
                      }
                      /** The returned string will look like 'A, B, ..., and C'  */
                      function formatListAnd(strings) {
                          return formatListCompound(strings, localizationOptions.restatementCompoundAnd);
                      }
                      valueFormatter.formatListAnd = formatListAnd;
                      /** The returned string will look like 'A, B, ..., or C' */
                      function formatListOr(strings) {
                          return formatListCompound(strings, localizationOptions.restatementCompoundOr);
                      }
                      valueFormatter.formatListOr = formatListOr;
                      function formatCore(options) {
                          var value = options.value, format = options.format, nullsAreBlank = options.nullsAreBlank, cultureSelector = options.cultureSelector;
                          var formattedValue = getStringFormat(value, nullsAreBlank ? nullsAreBlank : false);
                          if (!StringExtensions.isNullOrUndefinedOrWhiteSpaceString(formattedValue)) {
                              return formattedValue;
                          }
                          return formattingService.formatValue(value, format, cultureSelector);
                      }
                      function getStringFormat(value, nullsAreBlank) {
                          if (value == null && nullsAreBlank) {
                              return localizationOptions.null;
                          }
                          if (value === true) {
                              return localizationOptions.true;
                          }
                          if (value === false) {
                              return localizationOptions.false;
                          }
                          if (typeof value === "number" && isNaN(value)) {
                              return localizationOptions.NaN;
                          }
                          if (value === Number.NEGATIVE_INFINITY) {
                              return localizationOptions.negativeInfinity;
                          }
                          if (value === Number.POSITIVE_INFINITY) {
                              return localizationOptions.infinity;
                          }
                          return "";
                      }
                      function getDisplayUnits(displayUnitSystemType) {
                          var displayUnitSystem = createDisplayUnitSystem(displayUnitSystemType);
                          return displayUnitSystem.units;
                      }
                      valueFormatter.getDisplayUnits = getDisplayUnits;
                  })(valueFormatter = formatting.valueFormatter || (formatting.valueFormatter = {}));
              })(formatting = utils.formatting || (utils.formatting = {}));
          })(utils = extensibility.utils || (extensibility.utils = {}));
      })(extensibility = powerbi.extensibility || (powerbi.extensibility = {}));
  })(powerbi || (powerbi = {}));
  //# sourceMappingURL=index.js.map