// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @fileoverview This file defines a singleton which provides access to all data * that is available as soon as the page's resources are loaded (before DOM * content has finished loading). This data includes both localized strings and * any data that is important to have ready from a very early stage (e.g. things * that must be displayed right away). * * Note that loadTimeData is not guaranteed to be consistent between page * refreshes (https://crbug.com/740629) and should not contain values that might * change if the page is re-opened later. */ /** @type {!LoadTimeData} */ var loadTimeData; // Expose this type globally as a temporary work around until // https://github.com/google/closure-compiler/issues/544 is fixed. /** @constructor */ function LoadTimeData(){} (function() { 'use strict'; LoadTimeData.prototype = { /** * Sets the backing object. * * Note that there is no getter for |data_| to discourage abuse of the form: * * var value = loadTimeData.data()['key']; * * @param {Object} value The de-serialized page data. */ set data(value) { expect(!this.data_, 'Re-setting data.'); this.data_ = value; }, /** * Returns a JsEvalContext for |data_|. * @returns {JsEvalContext} */ createJsEvalContext: function() { return new JsEvalContext(this.data_); }, /** * @param {string} id An ID of a value that might exist. * @return {boolean} True if |id| is a key in the dictionary. */ valueExists: function(id) { return id in this.data_; }, /** * Fetches a value, expecting that it exists. * @param {string} id The key that identifies the desired value. * @return {*} The corresponding value. */ getValue: function(id) { expect(this.data_, 'No data. Did you remember to include strings.js?'); var value = this.data_[id]; expect(typeof value != 'undefined', 'Could not find value for ' + id); return value; }, /** * As above, but also makes sure that the value is a string. * @param {string} id The key that identifies the desired string. * @return {string} The corresponding string value. */ getString: function(id) { var value = this.getValue(id); expectIsType(id, value, 'string'); return /** @type {string} */ (value); }, /** * Returns a formatted localized string where $1 to $9 are replaced by the * second to the tenth argument. * @param {string} id The ID of the string we want. * @param {...(string|number)} var_args The extra values to include in the * formatted output. * @return {string} The formatted string. */ getStringF: function(id, var_args) { var value = this.getString(id); if (!value) return ''; var args = Array.prototype.slice.call(arguments); args[0] = value; return this.substituteString.apply(this, args); }, /** * Returns a formatted localized string where $1 to $9 are replaced by the * second to the tenth argument. Any standalone $ signs must be escaped as * $$. * @param {string} label The label to substitute through. * This is not an resource ID. * @param {...(string|number)} var_args The extra values to include in the * formatted output. * @return {string} The formatted string. */ substituteString: function(label, var_args) { var varArgs = arguments; return label.replace(/\$(.|$|\n)/g, function(m) { assert(m.match(/\$[$1-9]/), 'Unescaped $ found in localized string.'); return m == '$$' ? '$' : varArgs[m[1]]; }); }, /** * Returns a formatted string where $1 to $9 are replaced by the second to * tenth argument, split apart into a list of pieces describing how the * substitution was performed. Any standalone $ signs must be escaped as $$. * @param {string} label A localized string to substitute through. * This is not an resource ID. * @param {...(string|number)} var_args The extra values to include in the * formatted output. * @return {!Array} The formatted * string pieces. */ getSubstitutedStringPieces: function(label, var_args) { var varArgs = arguments; // Split the string by separately matching all occurrences of $1-9 and of // non $1-9 pieces. var pieces = (label.match(/(\$[1-9])|(([^$]|\$([^1-9]|$))+)/g) || []).map(function(p) { // Pieces that are not $1-9 should be returned after replacing $$ // with $. if (!p.match(/^\$[1-9]$/)) { assert( (p.match(/\$/g) || []).length % 2 == 0, 'Unescaped $ found in localized string.'); return {value: p.replace(/\$\$/g, '$'), arg: null}; } // Otherwise, return the substitution value. return {value: varArgs[p[1]], arg: p}; }); return pieces; }, /** * As above, but also makes sure that the value is a boolean. * @param {string} id The key that identifies the desired boolean. * @return {boolean} The corresponding boolean value. */ getBoolean: function(id) { var value = this.getValue(id); expectIsType(id, value, 'boolean'); return /** @type {boolean} */ (value); }, /** * As above, but also makes sure that the value is an integer. * @param {string} id The key that identifies the desired number. * @return {number} The corresponding number value. */ getInteger: function(id) { var value = this.getValue(id); expectIsType(id, value, 'number'); expect(value == Math.floor(value), 'Number isn\'t integer: ' + value); return /** @type {number} */ (value); }, /** * Override values in loadTimeData with the values found in |replacements|. * @param {Object} replacements The dictionary object of keys to replace. */ overrideValues: function(replacements) { expect( typeof replacements == 'object', 'Replacements must be a dictionary object.'); for (var key in replacements) { this.data_[key] = replacements[key]; } } }; /** * Checks condition, displays error message if expectation fails. * @param {*} condition The condition to check for truthiness. * @param {string} message The message to display if the check fails. */ function expect(condition, message) { if (!condition) { console.error( 'Unexpected condition on ' + document.location.href + ': ' + message); } } /** * Checks that the given value has the given type. * @param {string} id The id of the value (only used for error message). * @param {*} value The value to check the type on. * @param {string} type The type we expect |value| to be. */ function expectIsType(id, value, type) { expect( typeof value == type, '[' + value + '] (' + id + ') is not a ' + type); } expect(!loadTimeData, 'should only include this file once'); loadTimeData = new LoadTimeData; })();