1 | // Sect 9.2 Abstract Operations
|
2 | // ============================
|
3 |
|
4 | import {
|
5 | List,
|
6 | toObject,
|
7 | arrIndexOf,
|
8 | arrPush,
|
9 | arrSlice,
|
10 | Record,
|
11 | hop,
|
12 | defineProperty,
|
13 | } from "./util.js";
|
14 |
|
15 | import {
|
16 | IsStructurallyValidLanguageTag,
|
17 | CanonicalizeLanguageTag,
|
18 | DefaultLocale,
|
19 | } from "./6.locales-currencies-tz.js";
|
20 |
|
21 | const expUnicodeExSeq = /-u(?:-[0-9a-z]{2,8})+/gi; // See `extension` below
|
22 |
|
23 | export function /* 9.2.1 */CanonicalizeLocaleList (locales) {
|
24 | // The abstract operation CanonicalizeLocaleList takes the following steps:
|
25 |
|
26 | // 1. If locales is undefined, then a. Return a new empty List
|
27 | if (locales === undefined)
|
28 | return new List();
|
29 |
|
30 | // 2. Let seen be a new empty List.
|
31 | let seen = new List();
|
32 |
|
33 | // 3. If locales is a String value, then
|
34 | // a. Let locales be a new array created as if by the expression new
|
35 | // Array(locales) where Array is the standard built-in constructor with
|
36 | // that name and locales is the value of locales.
|
37 | locales = typeof locales === 'string' ? [ locales ] : locales;
|
38 |
|
39 | // 4. Let O be ToObject(locales).
|
40 | let O = toObject(locales);
|
41 |
|
42 | // 5. Let lenValue be the result of calling the [[Get]] internal method of
|
43 | // O with the argument "length".
|
44 | // 6. Let len be ToUint32(lenValue).
|
45 | let len = O.length;
|
46 |
|
47 | // 7. Let k be 0.
|
48 | let k = 0;
|
49 |
|
50 | // 8. Repeat, while k < len
|
51 | while (k < len) {
|
52 | // a. Let Pk be ToString(k).
|
53 | let Pk = String(k);
|
54 |
|
55 | // b. Let kPresent be the result of calling the [[HasProperty]] internal
|
56 | // method of O with argument Pk.
|
57 | let kPresent = Pk in O;
|
58 |
|
59 | // c. If kPresent is true, then
|
60 | if (kPresent) {
|
61 | // i. Let kValue be the result of calling the [[Get]] internal
|
62 | // method of O with argument Pk.
|
63 | let kValue = O[Pk];
|
64 |
|
65 | // ii. If the type of kValue is not String or Object, then throw a
|
66 | // TypeError exception.
|
67 | if (kValue === null || (typeof kValue !== 'string' && typeof kValue !== 'object'))
|
68 | throw new TypeError('String or Object type expected');
|
69 |
|
70 | // iii. Let tag be ToString(kValue).
|
71 | let tag = String(kValue);
|
72 |
|
73 | // iv. If the result of calling the abstract operation
|
74 | // IsStructurallyValidLanguageTag (defined in 6.2.2), passing tag as
|
75 | // the argument, is false, then throw a RangeError exception.
|
76 | if (!IsStructurallyValidLanguageTag(tag))
|
77 | throw new RangeError("'" + tag + "' is not a structurally valid language tag");
|
78 |
|
79 | // v. Let tag be the result of calling the abstract operation
|
80 | // CanonicalizeLanguageTag (defined in 6.2.3), passing tag as the
|
81 | // argument.
|
82 | tag = CanonicalizeLanguageTag(tag);
|
83 |
|
84 | // vi. If tag is not an element of seen, then append tag as the last
|
85 | // element of seen.
|
86 | if (arrIndexOf.call(seen, tag) === -1)
|
87 | arrPush.call(seen, tag);
|
88 | }
|
89 |
|
90 | // d. Increase k by 1.
|
91 | k++;
|
92 | }
|
93 |
|
94 | // 9. Return seen.
|
95 | return seen;
|
96 | }
|
97 |
|
98 | /**
|
99 | * The BestAvailableLocale abstract operation compares the provided argument
|
100 | * locale, which must be a String value with a structurally valid and
|
101 | * canonicalized BCP 47 language tag, against the locales in availableLocales and
|
102 | * returns either the longest non-empty prefix of locale that is an element of
|
103 | * availableLocales, or undefined if there is no such element. It uses the
|
104 | * fallback mechanism of RFC 4647, section 3.4. The following steps are taken:
|
105 | */
|
106 | export function /* 9.2.2 */BestAvailableLocale (availableLocales, locale) {
|
107 | // 1. Let candidate be locale
|
108 | let candidate = locale;
|
109 |
|
110 | // 2. Repeat
|
111 | while (candidate) {
|
112 | // a. If availableLocales contains an element equal to candidate, then return
|
113 | // candidate.
|
114 | if (arrIndexOf.call(availableLocales, candidate) > -1)
|
115 | return candidate;
|
116 |
|
117 | // b. Let pos be the character index of the last occurrence of "-"
|
118 | // (U+002D) within candidate. If that character does not occur, return
|
119 | // undefined.
|
120 | let pos = candidate.lastIndexOf('-');
|
121 |
|
122 | if (pos < 0)
|
123 | return;
|
124 |
|
125 | // c. If pos ≥ 2 and the character "-" occurs at index pos-2 of candidate,
|
126 | // then decrease pos by 2.
|
127 | if (pos >= 2 && candidate.charAt(pos - 2) === '-')
|
128 | pos -= 2;
|
129 |
|
130 | // d. Let candidate be the substring of candidate from position 0, inclusive,
|
131 | // to position pos, exclusive.
|
132 | candidate = candidate.substring(0, pos);
|
133 | }
|
134 | }
|
135 |
|
136 | /**
|
137 | * The LookupMatcher abstract operation compares requestedLocales, which must be
|
138 | * a List as returned by CanonicalizeLocaleList, against the locales in
|
139 | * availableLocales and determines the best available language to meet the
|
140 | * request. The following steps are taken:
|
141 | */
|
142 | export function /* 9.2.3 */LookupMatcher (availableLocales, requestedLocales) {
|
143 | // 1. Let i be 0.
|
144 | let i = 0;
|
145 |
|
146 | // 2. Let len be the number of elements in requestedLocales.
|
147 | let len = requestedLocales.length;
|
148 |
|
149 | // 3. Let availableLocale be undefined.
|
150 | let availableLocale;
|
151 |
|
152 | let locale, noExtensionsLocale;
|
153 |
|
154 | // 4. Repeat while i < len and availableLocale is undefined:
|
155 | while (i < len && !availableLocale) {
|
156 | // a. Let locale be the element of requestedLocales at 0-origined list
|
157 | // position i.
|
158 | locale = requestedLocales[i];
|
159 |
|
160 | // b. Let noExtensionsLocale be the String value that is locale with all
|
161 | // Unicode locale extension sequences removed.
|
162 | noExtensionsLocale = String(locale).replace(expUnicodeExSeq, '');
|
163 |
|
164 | // c. Let availableLocale be the result of calling the
|
165 | // BestAvailableLocale abstract operation (defined in 9.2.2) with
|
166 | // arguments availableLocales and noExtensionsLocale.
|
167 | availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
|
168 |
|
169 | // d. Increase i by 1.
|
170 | i++;
|
171 | }
|
172 |
|
173 | // 5. Let result be a new Record.
|
174 | let result = new Record();
|
175 |
|
176 | // 6. If availableLocale is not undefined, then
|
177 | if (availableLocale !== undefined) {
|
178 | // a. Set result.[[locale]] to availableLocale.
|
179 | result['[[locale]]'] = availableLocale;
|
180 |
|
181 | // b. If locale and noExtensionsLocale are not the same String value, then
|
182 | if (String(locale) !== String(noExtensionsLocale)) {
|
183 | // i. Let extension be the String value consisting of the first
|
184 | // substring of locale that is a Unicode locale extension sequence.
|
185 | let extension = locale.match(expUnicodeExSeq)[0];
|
186 |
|
187 | // ii. Let extensionIndex be the character position of the initial
|
188 | // "-" of the first Unicode locale extension sequence within locale.
|
189 | let extensionIndex = locale.indexOf('-u-');
|
190 |
|
191 | // iii. Set result.[[extension]] to extension.
|
192 | result['[[extension]]'] = extension;
|
193 |
|
194 | // iv. Set result.[[extensionIndex]] to extensionIndex.
|
195 | result['[[extensionIndex]]'] = extensionIndex;
|
196 | }
|
197 | }
|
198 | // 7. Else
|
199 | else
|
200 | // a. Set result.[[locale]] to the value returned by the DefaultLocale abstract
|
201 | // operation (defined in 6.2.4).
|
202 | result['[[locale]]'] = DefaultLocale();
|
203 |
|
204 | // 8. Return result
|
205 | return result;
|
206 | }
|
207 |
|
208 | /**
|
209 | * The BestFitMatcher abstract operation compares requestedLocales, which must be
|
210 | * a List as returned by CanonicalizeLocaleList, against the locales in
|
211 | * availableLocales and determines the best available language to meet the
|
212 | * request. The algorithm is implementation dependent, but should produce results
|
213 | * that a typical user of the requested locales would perceive as at least as
|
214 | * good as those produced by the LookupMatcher abstract operation. Options
|
215 | * specified through Unicode locale extension sequences must be ignored by the
|
216 | * algorithm. Information about such subsequences is returned separately.
|
217 | * The abstract operation returns a record with a [[locale]] field, whose value
|
218 | * is the language tag of the selected locale, which must be an element of
|
219 | * availableLocales. If the language tag of the request locale that led to the
|
220 | * selected locale contained a Unicode locale extension sequence, then the
|
221 | * returned record also contains an [[extension]] field whose value is the first
|
222 | * Unicode locale extension sequence, and an [[extensionIndex]] field whose value
|
223 | * is the index of the first Unicode locale extension sequence within the request
|
224 | * locale language tag.
|
225 | */
|
226 | export function /* 9.2.4 */BestFitMatcher (availableLocales, requestedLocales) {
|
227 | return LookupMatcher(availableLocales, requestedLocales);
|
228 | }
|
229 |
|
230 | /**
|
231 | * The ResolveLocale abstract operation compares a BCP 47 language priority list
|
232 | * requestedLocales against the locales in availableLocales and determines the
|
233 | * best available language to meet the request. availableLocales and
|
234 | * requestedLocales must be provided as List values, options as a Record.
|
235 | */
|
236 | export function /* 9.2.5 */ResolveLocale (availableLocales, requestedLocales, options, relevantExtensionKeys, localeData) {
|
237 | if (availableLocales.length === 0) {
|
238 | throw new ReferenceError('No locale data has been provided for this object yet.');
|
239 | }
|
240 |
|
241 | // The following steps are taken:
|
242 | // 1. Let matcher be the value of options.[[localeMatcher]].
|
243 | let matcher = options['[[localeMatcher]]'];
|
244 |
|
245 | let r;
|
246 |
|
247 | // 2. If matcher is "lookup", then
|
248 | if (matcher === 'lookup')
|
249 | // a. Let r be the result of calling the LookupMatcher abstract operation
|
250 | // (defined in 9.2.3) with arguments availableLocales and
|
251 | // requestedLocales.
|
252 | r = LookupMatcher(availableLocales, requestedLocales);
|
253 |
|
254 | // 3. Else
|
255 | else
|
256 | // a. Let r be the result of calling the BestFitMatcher abstract
|
257 | // operation (defined in 9.2.4) with arguments availableLocales and
|
258 | // requestedLocales.
|
259 | r = BestFitMatcher(availableLocales, requestedLocales);
|
260 |
|
261 | // 4. Let foundLocale be the value of r.[[locale]].
|
262 | let foundLocale = r['[[locale]]'];
|
263 |
|
264 | let extensionSubtags, extensionSubtagsLength;
|
265 |
|
266 | // 5. If r has an [[extension]] field, then
|
267 | if (hop.call(r, '[[extension]]')) {
|
268 | // a. Let extension be the value of r.[[extension]].
|
269 | let extension = r['[[extension]]'];
|
270 | // b. Let split be the standard built-in function object defined in ES5,
|
271 | // 15.5.4.14.
|
272 | let split = String.prototype.split;
|
273 | // c. Let extensionSubtags be the result of calling the [[Call]] internal
|
274 | // method of split with extension as the this value and an argument
|
275 | // list containing the single item "-".
|
276 | extensionSubtags = split.call(extension, '-');
|
277 | // d. Let extensionSubtagsLength be the result of calling the [[Get]]
|
278 | // internal method of extensionSubtags with argument "length".
|
279 | extensionSubtagsLength = extensionSubtags.length;
|
280 | }
|
281 |
|
282 | // 6. Let result be a new Record.
|
283 | let result = new Record();
|
284 |
|
285 | // 7. Set result.[[dataLocale]] to foundLocale.
|
286 | result['[[dataLocale]]'] = foundLocale;
|
287 |
|
288 | // 8. Let supportedExtension be "-u".
|
289 | let supportedExtension = '-u';
|
290 | // 9. Let i be 0.
|
291 | let i = 0;
|
292 | // 10. Let len be the result of calling the [[Get]] internal method of
|
293 | // relevantExtensionKeys with argument "length".
|
294 | let len = relevantExtensionKeys.length;
|
295 |
|
296 | // 11 Repeat while i < len:
|
297 | while (i < len) {
|
298 | // a. Let key be the result of calling the [[Get]] internal method of
|
299 | // relevantExtensionKeys with argument ToString(i).
|
300 | let key = relevantExtensionKeys[i];
|
301 | // b. Let foundLocaleData be the result of calling the [[Get]] internal
|
302 | // method of localeData with the argument foundLocale.
|
303 | let foundLocaleData = localeData[foundLocale];
|
304 | // c. Let keyLocaleData be the result of calling the [[Get]] internal
|
305 | // method of foundLocaleData with the argument key.
|
306 | let keyLocaleData = foundLocaleData[key];
|
307 | // d. Let value be the result of calling the [[Get]] internal method of
|
308 | // keyLocaleData with argument "0".
|
309 | let value = keyLocaleData['0'];
|
310 | // e. Let supportedExtensionAddition be "".
|
311 | let supportedExtensionAddition = '';
|
312 | // f. Let indexOf be the standard built-in function object defined in
|
313 | // ES5, 15.4.4.14.
|
314 | let indexOf = arrIndexOf;
|
315 |
|
316 | // g. If extensionSubtags is not undefined, then
|
317 | if (extensionSubtags !== undefined) {
|
318 | // i. Let keyPos be the result of calling the [[Call]] internal
|
319 | // method of indexOf with extensionSubtags as the this value and
|
320 | // an argument list containing the single item key.
|
321 | let keyPos = indexOf.call(extensionSubtags, key);
|
322 |
|
323 | // ii. If keyPos ≠ -1, then
|
324 | if (keyPos !== -1) {
|
325 | // 1. If keyPos + 1 < extensionSubtagsLength and the length of the
|
326 | // result of calling the [[Get]] internal method of
|
327 | // extensionSubtags with argument ToString(keyPos +1) is greater
|
328 | // than 2, then
|
329 | if (keyPos + 1 < extensionSubtagsLength
|
330 | && extensionSubtags[keyPos + 1].length > 2) {
|
331 | // a. Let requestedValue be the result of calling the [[Get]]
|
332 | // internal method of extensionSubtags with argument
|
333 | // ToString(keyPos + 1).
|
334 | let requestedValue = extensionSubtags[keyPos + 1];
|
335 | // b. Let valuePos be the result of calling the [[Call]]
|
336 | // internal method of indexOf with keyLocaleData as the
|
337 | // this value and an argument list containing the single
|
338 | // item requestedValue.
|
339 | let valuePos = indexOf.call(keyLocaleData, requestedValue);
|
340 |
|
341 | // c. If valuePos ≠ -1, then
|
342 | if (valuePos !== -1) {
|
343 | // i. Let value be requestedValue.
|
344 | value = requestedValue,
|
345 | // ii. Let supportedExtensionAddition be the
|
346 | // concatenation of "-", key, "-", and value.
|
347 | supportedExtensionAddition = '-' + key + '-' + value;
|
348 | }
|
349 | }
|
350 | // 2. Else
|
351 | else {
|
352 | // a. Let valuePos be the result of calling the [[Call]]
|
353 | // internal method of indexOf with keyLocaleData as the this
|
354 | // value and an argument list containing the single item
|
355 | // "true".
|
356 | let valuePos = indexOf(keyLocaleData, 'true');
|
357 |
|
358 | // b. If valuePos ≠ -1, then
|
359 | if (valuePos !== -1)
|
360 | // i. Let value be "true".
|
361 | value = 'true';
|
362 | }
|
363 | }
|
364 | }
|
365 | // h. If options has a field [[<key>]], then
|
366 | if (hop.call(options, '[[' + key + ']]')) {
|
367 | // i. Let optionsValue be the value of options.[[<key>]].
|
368 | let optionsValue = options['[[' + key + ']]'];
|
369 |
|
370 | // ii. If the result of calling the [[Call]] internal method of indexOf
|
371 | // with keyLocaleData as the this value and an argument list
|
372 | // containing the single item optionsValue is not -1, then
|
373 | if (indexOf.call(keyLocaleData, optionsValue) !== -1) {
|
374 | // 1. If optionsValue is not equal to value, then
|
375 | if (optionsValue !== value) {
|
376 | // a. Let value be optionsValue.
|
377 | value = optionsValue;
|
378 | // b. Let supportedExtensionAddition be "".
|
379 | supportedExtensionAddition = '';
|
380 | }
|
381 | }
|
382 | }
|
383 | // i. Set result.[[<key>]] to value.
|
384 | result['[[' + key + ']]'] = value;
|
385 |
|
386 | // j. Append supportedExtensionAddition to supportedExtension.
|
387 | supportedExtension += supportedExtensionAddition;
|
388 |
|
389 | // k. Increase i by 1.
|
390 | i++;
|
391 | }
|
392 | // 12. If the length of supportedExtension is greater than 2, then
|
393 | if (supportedExtension.length > 2) {
|
394 | // a.
|
395 | let privateIndex = foundLocale.indexOf("-x-");
|
396 | // b.
|
397 | if (privateIndex === -1) {
|
398 | // i.
|
399 | foundLocale = foundLocale + supportedExtension;
|
400 | }
|
401 | // c.
|
402 | else {
|
403 | // i.
|
404 | let preExtension = foundLocale.substring(0, privateIndex);
|
405 | // ii.
|
406 | let postExtension = foundLocale.substring(privateIndex);
|
407 | // iii.
|
408 | foundLocale = preExtension + supportedExtension + postExtension;
|
409 | }
|
410 | // d. asserting - skipping
|
411 | // e.
|
412 | foundLocale = CanonicalizeLanguageTag(foundLocale);
|
413 | }
|
414 | // 13. Set result.[[locale]] to foundLocale.
|
415 | result['[[locale]]'] = foundLocale;
|
416 |
|
417 | // 14. Return result.
|
418 | return result;
|
419 | }
|
420 |
|
421 | /**
|
422 | * The LookupSupportedLocales abstract operation returns the subset of the
|
423 | * provided BCP 47 language priority list requestedLocales for which
|
424 | * availableLocales has a matching locale when using the BCP 47 Lookup algorithm.
|
425 | * Locales appear in the same order in the returned list as in requestedLocales.
|
426 | * The following steps are taken:
|
427 | */
|
428 | export function /* 9.2.6 */LookupSupportedLocales (availableLocales, requestedLocales) {
|
429 | // 1. Let len be the number of elements in requestedLocales.
|
430 | let len = requestedLocales.length;
|
431 | // 2. Let subset be a new empty List.
|
432 | let subset = new List();
|
433 | // 3. Let k be 0.
|
434 | let k = 0;
|
435 |
|
436 | // 4. Repeat while k < len
|
437 | while (k < len) {
|
438 | // a. Let locale be the element of requestedLocales at 0-origined list
|
439 | // position k.
|
440 | let locale = requestedLocales[k];
|
441 | // b. Let noExtensionsLocale be the String value that is locale with all
|
442 | // Unicode locale extension sequences removed.
|
443 | let noExtensionsLocale = String(locale).replace(expUnicodeExSeq, '');
|
444 | // c. Let availableLocale be the result of calling the
|
445 | // BestAvailableLocale abstract operation (defined in 9.2.2) with
|
446 | // arguments availableLocales and noExtensionsLocale.
|
447 | let availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
|
448 |
|
449 | // d. If availableLocale is not undefined, then append locale to the end of
|
450 | // subset.
|
451 | if (availableLocale !== undefined)
|
452 | arrPush.call(subset, locale);
|
453 |
|
454 | // e. Increment k by 1.
|
455 | k++;
|
456 | }
|
457 |
|
458 | // 5. Let subsetArray be a new Array object whose elements are the same
|
459 | // values in the same order as the elements of subset.
|
460 | let subsetArray = arrSlice.call(subset);
|
461 |
|
462 | // 6. Return subsetArray.
|
463 | return subsetArray;
|
464 | }
|
465 |
|
466 | /**
|
467 | * The BestFitSupportedLocales abstract operation returns the subset of the
|
468 | * provided BCP 47 language priority list requestedLocales for which
|
469 | * availableLocales has a matching locale when using the Best Fit Matcher
|
470 | * algorithm. Locales appear in the same order in the returned list as in
|
471 | * requestedLocales. The steps taken are implementation dependent.
|
472 | */
|
473 | export function /*9.2.7 */BestFitSupportedLocales (availableLocales, requestedLocales) {
|
474 | // ###TODO: implement this function as described by the specification###
|
475 | return LookupSupportedLocales(availableLocales, requestedLocales);
|
476 | }
|
477 |
|
478 | /**
|
479 | * The SupportedLocales abstract operation returns the subset of the provided BCP
|
480 | * 47 language priority list requestedLocales for which availableLocales has a
|
481 | * matching locale. Two algorithms are available to match the locales: the Lookup
|
482 | * algorithm described in RFC 4647 section 3.4, and an implementation dependent
|
483 | * best-fit algorithm. Locales appear in the same order in the returned list as
|
484 | * in requestedLocales. The following steps are taken:
|
485 | */
|
486 | export function /*9.2.8 */SupportedLocales (availableLocales, requestedLocales, options) {
|
487 | let matcher, subset;
|
488 |
|
489 | // 1. If options is not undefined, then
|
490 | if (options !== undefined) {
|
491 | // a. Let options be ToObject(options).
|
492 | options = new Record(toObject(options));
|
493 | // b. Let matcher be the result of calling the [[Get]] internal method of
|
494 | // options with argument "localeMatcher".
|
495 | matcher = options.localeMatcher;
|
496 |
|
497 | // c. If matcher is not undefined, then
|
498 | if (matcher !== undefined) {
|
499 | // i. Let matcher be ToString(matcher).
|
500 | matcher = String(matcher);
|
501 |
|
502 | // ii. If matcher is not "lookup" or "best fit", then throw a RangeError
|
503 | // exception.
|
504 | if (matcher !== 'lookup' && matcher !== 'best fit')
|
505 | throw new RangeError('matcher should be "lookup" or "best fit"');
|
506 | }
|
507 | }
|
508 | // 2. If matcher is undefined or "best fit", then
|
509 | if (matcher === undefined || matcher === 'best fit')
|
510 | // a. Let subset be the result of calling the BestFitSupportedLocales
|
511 | // abstract operation (defined in 9.2.7) with arguments
|
512 | // availableLocales and requestedLocales.
|
513 | subset = BestFitSupportedLocales(availableLocales, requestedLocales);
|
514 | // 3. Else
|
515 | else
|
516 | // a. Let subset be the result of calling the LookupSupportedLocales
|
517 | // abstract operation (defined in 9.2.6) with arguments
|
518 | // availableLocales and requestedLocales.
|
519 | subset = LookupSupportedLocales(availableLocales, requestedLocales);
|
520 |
|
521 | // 4. For each named own property name P of subset,
|
522 | for (let P in subset) {
|
523 | if (!hop.call(subset, P))
|
524 | continue;
|
525 |
|
526 | // a. Let desc be the result of calling the [[GetOwnProperty]] internal
|
527 | // method of subset with P.
|
528 | // b. Set desc.[[Writable]] to false.
|
529 | // c. Set desc.[[Configurable]] to false.
|
530 | // d. Call the [[DefineOwnProperty]] internal method of subset with P, desc,
|
531 | // and true as arguments.
|
532 | defineProperty(subset, P, {
|
533 | writable: false, configurable: false, value: subset[P],
|
534 | });
|
535 | }
|
536 | // "Freeze" the array so no new elements can be added
|
537 | defineProperty(subset, 'length', { writable: false });
|
538 |
|
539 | // 5. Return subset
|
540 | return subset;
|
541 | }
|
542 |
|
543 | /**
|
544 | * The GetOption abstract operation extracts the value of the property named
|
545 | * property from the provided options object, converts it to the required type,
|
546 | * checks whether it is one of a List of allowed values, and fills in a fallback
|
547 | * value if necessary.
|
548 | */
|
549 | export function /*9.2.9 */GetOption (options, property, type, values, fallback) {
|
550 | // 1. Let value be the result of calling the [[Get]] internal method of
|
551 | // options with argument property.
|
552 | let value = options[property];
|
553 |
|
554 | // 2. If value is not undefined, then
|
555 | if (value !== undefined) {
|
556 | // a. Assert: type is "boolean" or "string".
|
557 | // b. If type is "boolean", then let value be ToBoolean(value).
|
558 | // c. If type is "string", then let value be ToString(value).
|
559 | value = type === 'boolean' ? Boolean(value)
|
560 | : (type === 'string' ? String(value) : value);
|
561 |
|
562 | // d. If values is not undefined, then
|
563 | if (values !== undefined) {
|
564 | // i. If values does not contain an element equal to value, then throw a
|
565 | // RangeError exception.
|
566 | if (arrIndexOf.call(values, value) === -1)
|
567 | throw new RangeError("'" + value + "' is not an allowed value for `" + property +'`');
|
568 | }
|
569 |
|
570 | // e. Return value.
|
571 | return value;
|
572 | }
|
573 | // Else return fallback.
|
574 | return fallback;
|
575 | }
|
576 |
|
577 | /**
|
578 | * The GetNumberOption abstract operation extracts a property value from the
|
579 | * provided options object, converts it to a Number value, checks whether it is
|
580 | * in the allowed range, and fills in a fallback value if necessary.
|
581 | */
|
582 | export function /* 9.2.10 */GetNumberOption (options, property, minimum, maximum, fallback) {
|
583 | // 1. Let value be the result of calling the [[Get]] internal method of
|
584 | // options with argument property.
|
585 | let value = options[property];
|
586 |
|
587 | // 2. If value is not undefined, then
|
588 | if (value !== undefined) {
|
589 | // a. Let value be ToNumber(value).
|
590 | value = Number(value);
|
591 |
|
592 | // b. If value is NaN or less than minimum or greater than maximum, throw a
|
593 | // RangeError exception.
|
594 | if (isNaN(value) || value < minimum || value > maximum)
|
595 | throw new RangeError('Value is not a number or outside accepted range');
|
596 |
|
597 | // c. Return floor(value).
|
598 | return Math.floor(value);
|
599 | }
|
600 | // 3. Else return fallback.
|
601 | return fallback;
|
602 | }
|