1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @fileoverview This file defines a singleton which provides access to all data
7 * that is available as soon as the page's resources are loaded (before DOM
8 * content has finished loading). This data includes both localized strings and
9 * any data that is important to have ready from a very early stage (e.g. things
10 * that must be displayed right away).
11 */
12
13var loadTimeData;
14
15// Expose this type globally as a temporary work around until
16// https://github.com/google/closure-compiler/issues/544 is fixed.
17/** @constructor */
18function LoadTimeData() {}
19
20(function() {
21  'use strict';
22
23  LoadTimeData.prototype = {
24    /**
25     * Sets the backing object.
26     *
27     * Note that there is no getter for |data_| to discourage abuse of the form:
28     *
29     *     var value = loadTimeData.data()['key'];
30     *
31     * @param {Object} value The de-serialized page data.
32     */
33    set data(value) {
34      expect(!this.data_, 'Re-setting data.');
35      this.data_ = value;
36    },
37
38    /**
39     * Returns a JsEvalContext for |data_|.
40     * @returns {JsEvalContext}
41     */
42    createJsEvalContext: function() {
43      return new JsEvalContext(this.data_);
44    },
45
46    /**
47     * @param {string} id An ID of a value that might exist.
48     * @return {boolean} True if |id| is a key in the dictionary.
49     */
50    valueExists: function(id) {
51      return id in this.data_;
52    },
53
54    /**
55     * Fetches a value, expecting that it exists.
56     * @param {string} id The key that identifies the desired value.
57     * @return {*} The corresponding value.
58     */
59    getValue: function(id) {
60      expect(this.data_, 'No data. Did you remember to include strings.js?');
61      var value = this.data_[id];
62      expect(typeof value != 'undefined', 'Could not find value for ' + id);
63      return value;
64    },
65
66    /**
67     * As above, but also makes sure that the value is a string.
68     * @param {string} id The key that identifies the desired string.
69     * @return {string} The corresponding string value.
70     */
71    getString: function(id) {
72      var value = this.getValue(id);
73      expectIsType(id, value, 'string');
74      return /** @type {string} */ (value);
75    },
76
77    /**
78     * Returns a formatted localized string where $1 to $9 are replaced by the
79     * second to the tenth argument.
80     * @param {string} id The ID of the string we want.
81     * @param {...string} var_args The extra values to include in the formatted
82     *     output.
83     * @return {string} The formatted string.
84     */
85    getStringF: function(id, var_args) {
86      var value = this.getString(id);
87      if (!value)
88        return '';
89
90      var varArgs = arguments;
91      return value.replace(/\$[$1-9]/g, function(m) {
92        return m == '$$' ? '$' : varArgs[m[1]];
93      });
94    },
95
96    /**
97     * As above, but also makes sure that the value is a boolean.
98     * @param {string} id The key that identifies the desired boolean.
99     * @return {boolean} The corresponding boolean value.
100     */
101    getBoolean: function(id) {
102      var value = this.getValue(id);
103      expectIsType(id, value, 'boolean');
104      return /** @type {boolean} */ (value);
105    },
106
107    /**
108     * As above, but also makes sure that the value is an integer.
109     * @param {string} id The key that identifies the desired number.
110     * @return {number} The corresponding number value.
111     */
112    getInteger: function(id) {
113      var value = this.getValue(id);
114      expectIsType(id, value, 'number');
115      expect(value == Math.floor(value), 'Number isn\'t integer: ' + value);
116      return /** @type {number} */ (value);
117    },
118
119    /**
120     * Override values in loadTimeData with the values found in |replacements|.
121     * @param {Object} replacements The dictionary object of keys to replace.
122     */
123    overrideValues: function(replacements) {
124      expect(typeof replacements == 'object',
125             'Replacements must be a dictionary object.');
126      for (var key in replacements) {
127        this.data_[key] = replacements[key];
128      }
129    }
130  };
131
132  /**
133   * Checks condition, displays error message if expectation fails.
134   * @param {*} condition The condition to check for truthiness.
135   * @param {string} message The message to display if the check fails.
136   */
137  function expect(condition, message) {
138    if (!condition) {
139      console.error('Unexpected condition on ' + document.location.href + ': ' +
140                    message);
141    }
142  }
143
144  /**
145   * Checks that the given value has the given type.
146   * @param {string} id The id of the value (only used for error message).
147   * @param {*} value The value to check the type on.
148   * @param {string} type The type we expect |value| to be.
149   */
150  function expectIsType(id, value, type) {
151    expect(typeof value == type, '[' + value + '] (' + id +
152                                 ') is not a ' + type);
153  }
154
155  expect(!loadTimeData, 'should only include this file once');
156  loadTimeData = new LoadTimeData;
157})();
158