1// Copyright 2013 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27// limitations under the License.
28
29// ECMAScript 402 API implementation is broken into separate files for
30// each service. The build system combines them together into one
31// Intl namespace.
32
33
34// Save references to Intl objects and methods we use, for added security.
35var savedObjects = {
36  'collator': Intl.Collator,
37  'numberformat': Intl.NumberFormat,
38  'dateformatall': Intl.DateTimeFormat,
39  'dateformatdate': Intl.DateTimeFormat,
40  'dateformattime': Intl.DateTimeFormat
41};
42
43
44// Default (created with undefined locales and options parameters) collator,
45// number and date format instances. They'll be created as needed.
46var defaultObjects = {
47  'collator': undefined,
48  'numberformat': undefined,
49  'dateformatall': undefined,
50  'dateformatdate': undefined,
51  'dateformattime': undefined,
52};
53
54
55/**
56 * Returns cached or newly created instance of a given service.
57 * We cache only default instances (where no locales or options are provided).
58 */
59function cachedOrNewService(service, locales, options, defaults) {
60  var useOptions = (defaults === undefined) ? options : defaults;
61  if (locales === undefined && options === undefined) {
62    if (defaultObjects[service] === undefined) {
63      defaultObjects[service] = new savedObjects[service](locales, useOptions);
64    }
65    return defaultObjects[service];
66  }
67  return new savedObjects[service](locales, useOptions);
68}
69
70
71/**
72 * Compares this and that, and returns less than 0, 0 or greater than 0 value.
73 * Overrides the built-in method.
74 */
75Object.defineProperty(String.prototype, 'localeCompare', {
76  value: function(that) {
77    if (%_IsConstructCall()) {
78      throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
79    }
80
81    if (this === undefined || this === null) {
82      throw new TypeError('Method invoked on undefined or null value.');
83    }
84
85    var locales = arguments[1];
86    var options = arguments[2];
87    var collator = cachedOrNewService('collator', locales, options);
88    return compare(collator, this, that);
89  },
90  writable: true,
91  configurable: true,
92  enumerable: false
93});
94%FunctionSetName(String.prototype.localeCompare, 'localeCompare');
95%FunctionRemovePrototype(String.prototype.localeCompare);
96%SetNativeFlag(String.prototype.localeCompare);
97
98
99/**
100 * Formats a Number object (this) using locale and options values.
101 * If locale or options are omitted, defaults are used.
102 */
103Object.defineProperty(Number.prototype, 'toLocaleString', {
104  value: function() {
105    if (%_IsConstructCall()) {
106      throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
107    }
108
109    if (!(this instanceof Number) && typeof(this) !== 'number') {
110      throw new TypeError('Method invoked on an object that is not Number.');
111    }
112
113    var locales = arguments[0];
114    var options = arguments[1];
115    var numberFormat = cachedOrNewService('numberformat', locales, options);
116    return formatNumber(numberFormat, this);
117  },
118  writable: true,
119  configurable: true,
120  enumerable: false
121});
122%FunctionSetName(Number.prototype.toLocaleString, 'toLocaleString');
123%FunctionRemovePrototype(Number.prototype.toLocaleString);
124%SetNativeFlag(Number.prototype.toLocaleString);
125
126
127/**
128 * Returns actual formatted date or fails if date parameter is invalid.
129 */
130function toLocaleDateTime(date, locales, options, required, defaults, service) {
131  if (!(date instanceof Date)) {
132    throw new TypeError('Method invoked on an object that is not Date.');
133  }
134
135  if (isNaN(date)) {
136    return 'Invalid Date';
137  }
138
139  var internalOptions = toDateTimeOptions(options, required, defaults);
140
141  var dateFormat =
142      cachedOrNewService(service, locales, options, internalOptions);
143
144  return formatDate(dateFormat, date);
145}
146
147
148/**
149 * Formats a Date object (this) using locale and options values.
150 * If locale or options are omitted, defaults are used - both date and time are
151 * present in the output.
152 */
153Object.defineProperty(Date.prototype, 'toLocaleString', {
154  value: function() {
155    if (%_IsConstructCall()) {
156      throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
157    }
158
159    var locales = arguments[0];
160    var options = arguments[1];
161    return toLocaleDateTime(
162        this, locales, options, 'any', 'all', 'dateformatall');
163  },
164  writable: true,
165  configurable: true,
166  enumerable: false
167});
168%FunctionSetName(Date.prototype.toLocaleString, 'toLocaleString');
169%FunctionRemovePrototype(Date.prototype.toLocaleString);
170%SetNativeFlag(Date.prototype.toLocaleString);
171
172
173/**
174 * Formats a Date object (this) using locale and options values.
175 * If locale or options are omitted, defaults are used - only date is present
176 * in the output.
177 */
178Object.defineProperty(Date.prototype, 'toLocaleDateString', {
179  value: function() {
180    if (%_IsConstructCall()) {
181      throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
182    }
183
184    var locales = arguments[0];
185    var options = arguments[1];
186    return toLocaleDateTime(
187        this, locales, options, 'date', 'date', 'dateformatdate');
188  },
189  writable: true,
190  configurable: true,
191  enumerable: false
192});
193%FunctionSetName(Date.prototype.toLocaleDateString, 'toLocaleDateString');
194%FunctionRemovePrototype(Date.prototype.toLocaleDateString);
195%SetNativeFlag(Date.prototype.toLocaleDateString);
196
197
198/**
199 * Formats a Date object (this) using locale and options values.
200 * If locale or options are omitted, defaults are used - only time is present
201 * in the output.
202 */
203Object.defineProperty(Date.prototype, 'toLocaleTimeString', {
204  value: function() {
205    if (%_IsConstructCall()) {
206      throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
207    }
208
209    var locales = arguments[0];
210    var options = arguments[1];
211    return toLocaleDateTime(
212        this, locales, options, 'time', 'time', 'dateformattime');
213  },
214  writable: true,
215  configurable: true,
216  enumerable: false
217});
218%FunctionSetName(Date.prototype.toLocaleTimeString, 'toLocaleTimeString');
219%FunctionRemovePrototype(Date.prototype.toLocaleTimeString);
220%SetNativeFlag(Date.prototype.toLocaleTimeString);
221