text: // Copyright 2010 The Closure Library Authors. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS-IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  /**
   * @fileoverview Creates a string of a JSON object, properly indented for
   * display.
   *
   */
  
  goog.provide('goog.format.JsonPrettyPrinter');
  goog.provide('goog.format.JsonPrettyPrinter.HtmlDelimiters');
  goog.provide('goog.format.JsonPrettyPrinter.TextDelimiters');
  
  goog.require('goog.json');
  goog.require('goog.json.Serializer');
  goog.require('goog.string');
  goog.require('goog.string.StringBuffer');
  goog.require('goog.string.format');
  
  
  
  /**
   * Formats a JSON object as a string, properly indented for display.  Supports
   * displaying the string as text or html.  Users can also specify their own
   * set of delimiters for different environments.  For example, the JSON object:
   *
   * <code>{"a": 1, "b": {"c": null, "d": true, "e": [1, 2]}}</code>
   *
   * Will be displayed like this:
   *
   * <code>{
   *   "a": 1,
   *   "b": {
   *     "c": null,
   *     "d": true,
   *     "e": [
   *       1,
   *       2
   *     ]
   *   }
   * }</code>
   * @param {goog.format.JsonPrettyPrinter.TextDelimiters} delimiters Container
   *     for the various strings to use to delimit objects, arrays, newlines, and
   *     other pieces of the output.
   * @constructor
   */
  goog.format.JsonPrettyPrinter = function(delimiters) {
  
    /**
     * The set of characters to use as delimiters.
     * @type {goog.format.JsonPrettyPrinter.TextDelimiters}
     * @private
     */
    this.delimiters_ = delimiters ||
        new goog.format.JsonPrettyPrinter.TextDelimiters();
  
    /**
     * Used to serialize property names and values.
     * @type {goog.json.Serializer}
     * @private
     */
    this.jsonSerializer_ = new goog.json.Serializer();
  };
  
  
  /**
   * Formats a JSON object as a string, properly indented for display.
   * @param {*} json The object to pretty print. It could be a JSON object, a
   *     string representing a JSON object, or any other type.
   * @return {string} Returns a string of the JSON object, properly indented for
   *     display.
   */
  goog.format.JsonPrettyPrinter.prototype.format = function(json) {
    // If input is undefined, null, or empty, return an empty string.
    if (!goog.isDefAndNotNull(json)) {
      return '';
    }
    if (goog.isString(json)) {
      if (goog.string.isEmpty(json)) {
        return '';
      }
      // Try to coerce a string into a JSON object.
      json = goog.json.parse(json);
    }
    var outputBuffer = new goog.string.StringBuffer();
    this.printObject_(json, outputBuffer, 0);
    return outputBuffer.toString();
  };
  
  
  /**
   * Formats a property value based on the type of the propery.
   * @param {*} val The object to format.
   * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
   *     response to.
   * @param {number} indent The number of spaces to indent each line of the
   *     output.
   * @private
   */
  goog.format.JsonPrettyPrinter.prototype.printObject_ = function(val,
      outputBuffer, indent) {
    var typeOf = goog.typeOf(val);
    switch (typeOf) {
      case 'null':
      case 'boolean':
      case 'number':
      case 'string':
        // "null", "boolean", "number" and "string" properties are printed
        // directly to the output.
        this.printValue_(
            /** @type {null|string|boolean|number} */ (val),
            typeOf, outputBuffer);
        break;
      case 'array':
        // Example of how an array looks when formatted
        // (using the default delimiters):
        // [
        //   1,
        //   2,
        //   3
        // ]
        outputBuffer.append(this.delimiters_.arrayStart);
        var i = 0;
        // Iterate through the array and format each element.
        for (i = 0; i < val.length; i++) {
          if (i > 0) {
            // There are multiple elements, add a comma to separate them.
            outputBuffer.append(this.delimiters_.propertySeparator);
          }
          outputBuffer.append(this.delimiters_.lineBreak);
          this.printSpaces_(indent + this.delimiters_.indent, outputBuffer);
          this.printObject_(val[i], outputBuffer,
              indent + this.delimiters_.indent);
        }
        // If there are no properties in this object, don't put a line break
        // between the beginning "[" and ending "]", so the output of an empty
        // array looks like <code>[]</code>.
        if (i > 0) {
          outputBuffer.append(this.delimiters_.lineBreak);
          this.printSpaces_(indent, outputBuffer);
        }
        outputBuffer.append(this.delimiters_.arrayEnd);
        break;
      case 'object':
        // Example of how an object looks when formatted
        // (using the default delimiters):
        // {
        //   "a": 1,
        //   "b": 2,
        //   "c": "3"
        // }
        outputBuffer.append(this.delimiters_.objectStart);
        var propertyCount = 0;
        // Iterate through the object and display each property.
        for (var name in val) {
          if (!val.hasOwnProperty(name)) {
            continue;
          }
          if (propertyCount > 0) {
            // There are multiple properties, add a comma to separate them.
            outputBuffer.append(this.delimiters_.propertySeparator);
          }
          outputBuffer.append(this.delimiters_.lineBreak);
          this.printSpaces_(indent + this.delimiters_.indent, outputBuffer);
          this.printName_(name, outputBuffer);
          outputBuffer.append(this.delimiters_.nameValueSeparator,
              this.delimiters_.space);
          this.printObject_(val[name], outputBuffer,
              indent + this.delimiters_.indent);
          propertyCount++;
        }
        // If there are no properties in this object, don't put a line break
        // between the beginning "{" and ending "}", so the output of an empty
        // object looks like <code>{}</code>.
        if (propertyCount > 0) {
          outputBuffer.append(this.delimiters_.lineBreak);
          this.printSpaces_(indent, outputBuffer);
        }
        outputBuffer.append(this.delimiters_.objectEnd);
        break;
      // Other types, such as "function", aren't expected in JSON, and their
      // behavior is undefined.  In these cases, just print an empty string to the
      // output buffer.  This allows the pretty printer to continue while still
      // outputing well-formed JSON.
      default:
        this.printValue_('', 'unknown', outputBuffer);
    }
  };
  
  
  /**
   * Prints a property name to the output.
   * @param {string} name The property name.
   * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
   *     response to.
   * @private
   */
  goog.format.JsonPrettyPrinter.prototype.printName_ = function(name,
      outputBuffer) {
    outputBuffer.append(this.delimiters_.preName,
        this.jsonSerializer_.serialize(name), this.delimiters_.postName);
  };
  
  
  /**
   * Prints a property name to the output.
   * @param {string|boolean|number|null} val The property value.
   * @param {string} typeOf The type of the value.  Used to customize
   *     value-specific css in the display.  This allows clients to distinguish
   *     between different types in css.  For example, the client may define two
   *     classes: "goog-jsonprettyprinter-propertyvalue-string" and
   *     "goog-jsonprettyprinter-propertyvalue-number" to assign a different color
   *     to string and number values.
   * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
   *     response to.
   * @private
   */
  goog.format.JsonPrettyPrinter.prototype.printValue_ = function(val,
      typeOf, outputBuffer) {
    outputBuffer.append(goog.string.format(this.delimiters_.preValue, typeOf),
        this.jsonSerializer_.serialize(val),
        goog.string.format(this.delimiters_.postValue, typeOf));
  };
  
  
  /**
   * Print a number of space characters to the output.
   * @param {number} indent The number of spaces to indent the line.
   * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
   *     response to.
   * @private
   */
  goog.format.JsonPrettyPrinter.prototype.printSpaces_ = function(indent,
      outputBuffer) {
    outputBuffer.append(goog.string.repeat(this.delimiters_.space, indent));
  };
  
  
  
  /**
   * A container for the delimiting characters used to display the JSON string
   * to a text display.  Each delimiter is a publicly accessible property of
   * the object, which makes it easy to tweak delimiters to specific environments.
   * @constructor
   */
  goog.format.JsonPrettyPrinter.TextDelimiters = function() {
  };
  
  
  /**
   * Represents a space character in the output.  Used to indent properties a
   * certain number of spaces, and to separate property names from property
   * values.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.space = ' ';
  
  
  /**
   * Represents a newline character in the output.  Used to begin a new line.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.lineBreak = '\n';
  
  
  /**
   * Represents the start of an object in the output.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectStart = '{';
  
  
  /**
   * Represents the end of an object in the output.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectEnd = '}';
  
  
  /**
   * Represents the start of an array in the output.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayStart = '[';
  
  
  /**
   * Represents the end of an array in the output.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayEnd = ']';
  
  
  /**
   * Represents the string used to separate properties in the output.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.propertySeparator = ',';
  
  
  /**
   * Represents the string used to separate property names from property values in
   * the output.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.nameValueSeparator = ':';
  
  
  /**
   * A string that's placed before a property name in the output.  Useful for
   * wrapping a property name in an html tag.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preName = '';
  
  
  /**
   * A string that's placed after a property name in the output.  Useful for
   * wrapping a property name in an html tag.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postName = '';
  
  
  /**
   * A string that's placed before a property value in the output.  Useful for
   * wrapping a property value in an html tag.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preValue = '';
  
  
  /**
   * A string that's placed after a property value in the output.  Useful for
   * wrapping a property value in an html tag.
   * @type {string}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postValue = '';
  
  
  /**
   * Represents the number of spaces to indent each sub-property of the JSON.
   * @type {number}
   */
  goog.format.JsonPrettyPrinter.TextDelimiters.prototype.indent = 2;
  
  
  
  /**
   * A container for the delimiting characters used to display the JSON string
   * to an HTML <code>&lt;pre&gt;</code> or <code>&lt;code&gt;</code> element.
   * @constructor
   * @extends {goog.format.JsonPrettyPrinter.TextDelimiters}
   */
  goog.format.JsonPrettyPrinter.HtmlDelimiters = function() {
    goog.format.JsonPrettyPrinter.TextDelimiters.call(this);
  };
  goog.inherits(goog.format.JsonPrettyPrinter.HtmlDelimiters,
      goog.format.JsonPrettyPrinter.TextDelimiters);
  
  
  /**
   * A <code>span</code> tag thats placed before a property name.  Used to style
   * property names with CSS.
   * @type {string}
   * @override
   */
  goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.preName =
      '<span class="' +
      goog.getCssName('goog-jsonprettyprinter-propertyname') +
      '">';
  
  
  /**
   * A closing <code>span</code> tag that's placed after a property name.
   * @type {string}
   * @override
   */
  goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.postName = '</span>';
  
  
  /**
   * A <code>span</code> tag thats placed before a property value.  Used to style
   * property value with CSS.  The span tag's class is in the format
   * goog-jsonprettyprinter-propertyvalue-{TYPE}, where {TYPE} is the JavaScript
   * type of the object (the {TYPE} parameter is obtained from goog.typeOf).  This
   * can be used to style different value types.
   * @type {string}
   * @override
   */
  goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.preValue =
      '<span class="' +
      goog.getCssName('goog-jsonprettyprinter-propertyvalue') +
      '-%s">';
  
  
  /**
   * A closing <code>span</code> tag that's placed after a property value.
   * @type {string}
   * @override
   */
  goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.postValue = '</span>';
  